'From etoys2.2 of 21 September 2007 [latest update: #1756] on 6 November 2007 at 7:04:57 pm'! "Change Set: sharing-bf Date: 14 October 2007 Author: Bert Freudenberg Integrate with presence service: Etoys instances will be visible in the mesh when shared, and must be joined by clicking the icon in the mesh view."! AbstractLauncher subclass: #SugarLauncher instanceVariableNames: 'process dbus buddies handlers sharedActivity ' classVariableNames: 'Current ' poolDictionaries: '' category: 'Sugar'! !SugarLauncher commentStamp: '' prior: 0! SugarLauncher handles communication with Sugar in the OLPC environment. It starts a background process running the DBus mainloop which receives messages (or replies to our own sends). Since messages are handled in a background process, communication with the UI process must be asynchronous. The DBus communication is used for, e.g., storing to the Journal (datastore), and networking (presence). We also handle events from the window system. ! !SugarLauncher methodsFor: 'running' stamp: 'bf 11/5/2007 16:33'! shutDown sharedActivity ifNotNil: [ self leaveSharedActivity. sharedActivity := nil]. Project allSubInstancesDo: [:prj | prj removeParameter: #sugarId]. ServerDirectory inImageServers keysAndValuesDo: [:srvrName :srvr | (srvr isKindOf: SugarDatastoreDirectory) ifTrue: [ ServerDirectory removeServerNamed: srvrName ifAbsent: []]]. Current := nil. World windowEventHandler: nil. process ifNotNil: [ process terminate. process := nil]. buddies := Dictionary new. handlers := Dictionary new. ! ! !SugarLauncher methodsFor: 'running' stamp: 'bf 11/1/2007 17:46'! startUp self class allInstances do: [:ea | ea shutDown]. Current := self. parameters at: 'ACTIVITY_ID' ifPresent: [ :activityId | World windowEventHandler: self. process := [self runDBusService: 'org.laptop.Activity', activityId] forkAt: Processor userInterruptPriority named: 'Sugar DBus service'. ServerDirectory addServer: (SugarDatastoreDirectory mimetype: 'application/x-squeak-project' extension: '.pr') named: SugarLauncher defaultDatastoreDirName. sharedActivity := self getSharedActivityById: activityId. sharedActivity ifNotNil: [self joinSharedActivity]. parameters at: 'OBJECT_ID' ifPresent: [:id | ^self resumeJournalEntry: id]. Project current projectParameterAt: #sugarAutoSave put: true. self createJournalEntryFor: Project current filename: '' mimetype: ''. ^self welcome: (parameters at: 'URI' ifAbsent: [''])]. self welcome: '' ! ! !SugarLauncher methodsFor: 'commands' stamp: 'bf 11/1/2007 17:48'! quit self leaveSharedActivity. Project current projectParameterAt: #sugarAutoSave put: true; storeOnServerWithNoInteractionThenQuit. ! ! !SugarLauncher methodsFor: 'commands' stamp: 'bf 11/6/2007 18:44'! share sharedActivity ifNotNil: [^self]. sharedActivity := self shareActivityId: (parameters at: 'ACTIVITY_ID') bundleId: (parameters at: 'BUNDLE_ID') name: (self titleFromProject: Project current) squeakToUtf8 properties: Dictionary new. "due to bug 4660 we can't pass properties directly" self setSharedActivityProperties: ({'private' -> false} as: Dictionary).! ! !SugarLauncher methodsFor: 'accessing' stamp: 'bf 11/6/2007 18:15'! buddies sharedActivity ifNotNil: [^self getSharedActivityBuddies]. ^buddies ifNil: [buddies := Dictionary new]! ! !SugarLauncher methodsFor: 'dbus' stamp: 'bf 11/5/2007 16:26'! runDBusService: aString | msg error serviceName | serviceName := aString. dbus := DBusConnection connectToSessionBus. dbus registerName: serviceName. dbus registerSemaphore: Semaphore new. [[ [dbus dataRemains] whileTrue: [ (msg := dbus popMessage) ifNotNil: [ ((self dispatchDBusMessage: msg) not and: [msg isMethodCall]) ifTrue: [ error := DBusMessageError newFor: msg name: (DBusMessageError dbusErrorUnknownMethod) withMessage: (parameters at: 'BUNDLE_ID'), ' does not understand ', msg selector, '()'. error ifNotNil: [dbus sendMessage: error]]. ]]. dbus readSemaphore wait. ] repeat] ensure: [dbus releaseName: serviceName] ! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 11/1/2007 17:06'! propertiesFrom: aProject | preview autoSave | preview := [(Base64MimeConverter mimeEncode: ( ByteArray streamContents: [:s | PNGReadWriter putForm: (aProject thumbnail asFormOfDepth: 16) onStream: s]) readStream) contents] ifError: ['']. autoSave := aProject projectParameterAt: #sugarAutoSave ifAbsent: [true]. ^ { 'activity' -> (parameters at: 'BUNDLE_ID'). 'activity_id' -> (autoSave ifTrue: [parameters at: 'ACTIVITY_ID'] ifFalse: ['']). "temp hack for trial-3" 'title:text' -> (self titleFromProject: aProject) squeakToUtf8. 'title_set_by_user' -> (aProject currentVersionNumber>0 ifTrue: ['1'] ifFalse: ['0']). 'keep' -> (autoSave ifTrue: ['0'] ifFalse: ['1']). 'mtime' -> (DateAndTime now asString first: 19). 'preview' -> preview. 'icon-color' -> self ownerBuddy colors. } as: Dictionary. ! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 11/1/2007 17:06'! titleFromProject: aProject ^((aProject name beginsWith: 'Unnamed' translated) ifTrue: ['Etoys Project' translated] ifFalse: [aProject name]) squeakToUtf8. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/6/2007 18:15'! getSharedActivityBuddies | reply buddiePathes | reply := self sendDBusMessage: (DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: sharedActivity interface: 'org.laptop.Sugar.Presence.Activity' selector: 'GetJoinedBuddies'). buddiePathes := reply arguments first fromDBusArgument. ^buddiePathes collect: [:objPath | reply := self sendDBusMessage: (DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: objPath interface: 'org.laptop.Sugar.Presence.Buddy' selector: 'GetProperties'). SugarBuddy fromDictionary: reply arguments first fromDBusArgument]! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/1/2007 16:36'! getSharedActivityById: aString | reply | [reply := self sendDBusMessage: ((DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: '/org/laptop/Sugar/Presence' interface: 'org.laptop.Sugar.Presence' selector: 'GetActivityById') addArgument: aString signature: 's') ] ifError: [^nil]. ^reply arguments first fromDBusArgument. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/1/2007 17:52'! getSharedActivityProperties | reply | reply := self sendDBusMessage: (DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: sharedActivity interface: 'org.laptop.Sugar.Presence.Activity' selector: 'GetProperties'). ^reply arguments first fromDBusArgument. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/1/2007 17:47'! joinSharedActivity [self sendDBusMessage: (DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: sharedActivity interface: 'org.laptop.Sugar.Presence.Activity' selector: 'Join') ] ifError: [sharedActivity := nil] ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/1/2007 17:45'! leaveSharedActivity sharedActivity ifNotNil: [ self sendDBusMessage: (DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: sharedActivity interface: 'org.laptop.Sugar.Presence.Activity' selector: 'Leave') onSuccess: [:msg | ] onError: [:msg | ]. sharedActivity := nil]. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/6/2007 15:29'! setSharedActivityProperties: aDictionary self sendDBusMessage: ((DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: sharedActivity interface: 'org.laptop.Sugar.Presence.Activity' selector: 'SetProperties') addArgument: aDictionary signature: 'a{sv}'). ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 11/1/2007 16:33'! shareActivityId: activityId bundleId: bundleId name: nameString properties: propertyDict | reply | [reply := self sendDBusMessage: ((DBusMessageMethodCall destination: 'org.laptop.Sugar.Presence' path: '/org/laptop/Sugar/Presence' interface: 'org.laptop.Sugar.Presence' selector: 'ShareActivity') addArgument: activityId signature: 's'; addArgument: bundleId signature: 's'; addArgument: nameString signature: 's'; addArgument: propertyDict signature: 'a{sv}' ) ] ifError: [^nil]. ^reply arguments first fromDBusArgument. ! ! !SugarLauncher methodsFor: 'testing' stamp: 'bf 11/6/2007 15:30'! isShared ^sharedActivity notNil! ! !SugarLauncher class methodsFor: 'class initialization' stamp: 'bf 11/5/2007 16:57'! initialize "self initialize" "Our startUp is handled by AutoStart, but not shutDown" Smalltalk addToShutDownList: self after: AutoStart! ! !SugarLauncher class methodsFor: 'class initialization' stamp: 'bf 11/5/2007 16:56'! shutDown Current ifNotNil: [Current shutDown]! ! !SugarNavigatorBar methodsFor: 'button actions' stamp: 'bf 11/6/2007 18:50'! shareThisWorld SugarLauncher current isShared ifFalse: [self startSharing]. self getBadge. ! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'bf 11/6/2007 18:39'! startSharing listener ifNotNil: [listener stopListening]. listener ifNil: [listener _ SugarListenerMorph new]. listener position: -200@-200. ActiveWorld addMorphBack: listener. SugarLauncher current share. listener startListening. ActiveWorld remoteServer: nil. ActiveWorld submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. NebraskaServerMorph serveWorld. SugarLibrary default recolorButton: shareButton for: 'share' baseColor: self color highLightColor: self highlightColor. ! ! !SugarNavigatorBar methodsFor: 'sharing' stamp: 'bf 11/6/2007 18:39'! stopSharing SugarLauncher current leaveSharedActivity. listener ifNotNil: [listener stopListening. listener _ nil]. ActiveWorld remoteServer: nil. ActiveWorld submorphs do: [:e | (e isMemberOf: NebraskaServerMorph) ifTrue: [e delete]]. SugarLibrary default recolorButton: shareButton for: 'private' baseColor: self color highLightColor: self highlightColor. ! ! SugarLauncher initialize! !SugarLauncher class reorganize! ('utilities' compileDBusMessageRegistry defaultDatastoreDirName) ('accessing' current) ('testing' isRunningInSugar) ('class initialization' initialize shutDown) ! SugarLauncher removeSelector: #buddiesFromDBus! SugarLauncher removeSelector: #getActivityById:! SugarLauncher removeSelector: #getActivityProperties:! SugarLauncher removeSelector: #getSharedActivityProperties:! SugarLauncher removeSelector: #joinActivity:! SugarLauncher removeSelector: #joinSharedActivity:! SugarLauncher removeSelector: #leaveSharedActivity:! AbstractLauncher subclass: #SugarLauncher instanceVariableNames: 'process dbus buddies handlers sharedActivity' classVariableNames: 'Current' poolDictionaries: '' category: 'Sugar'!