'From etoys3.0 of 7 March 2008 [latest update: #2020] on 19 June 2008 at 10:04:34 pm'! "Change Set: dbusSugar-bf Date: 19 June 2008 Author: Bert Freudenberg switch to new DBus bindings"! DBusObject subclass: #SugarEtoysActivity instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Sugar-DBus'! !SugarEtoysActivity commentStamp: '' prior: 0! An instance of me is exported on the DBus to provide services callable from Sugar. Currently it simply forwards to the current SugarLauncher.! AbstractLauncher subclass: #SugarLauncher instanceVariableNames: 'process dbus buddies handlers sharedActivity ' classVariableNames: 'Current UISema ' poolDictionaries: '' category: 'Sugar'! !SugarLauncher commentStamp: '' prior: 0! SugarLauncher handles communication with Sugar in the OLPC environment. The DBus communication is used for, e.g., storing to the Journal (datastore), and networking (presence). We also handle events from the window system. ! !SugarDatastoreDirectory methodsFor: 'file directory' stamp: 'bf 6/19/2008 18:16'! entries "Return a collection of directory entries for the files and directories in this directory." ^(SugarLauncher current findJournalEntries: self query properties: #('uid' 'title' 'ctime' 'mtime' 'file-size')) collect: [:props | DirectoryEntry name: (((props at: 'title') copyReplaceAll: '/' with: '\') contractTo: 64) utf8ToSqueak, '-', (props at: 'uid'), self extension creationTime: ([(DateAndTime fromString: (props at: 'ctime')) asSeconds] ifError: [0]) modificationTime: ([(DateAndTime fromString: (props at: 'mtime')) asSeconds] ifError: [0]) isDirectory: false fileSize: (props at: 'file-size' ifAbsent: [42]) ]! ! !SugarEtoysActivity methodsFor: 'dbus methods' stamp: 'bf 4/30/2008 16:19'! setActive: aBoolean self dbusMethod: 'org.laptop.Activity.SetActive'. SugarLauncher current active: aBoolean! ! !SugarEtoysActivity methodsFor: 'dbus methods' stamp: 'bf 4/30/2008 16:46'! takeScreenshot self dbusMethod: 'org.laptop.Activity.TakeScreenshot<>'. SugarLauncher current takeScreenshot! ! !SugarLauncher methodsFor: 'running' stamp: 'bf 6/19/2008 17:03'! 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. ! ! !SugarLauncher methodsFor: 'running' stamp: 'bf 6/19/2008 18:54'! startUp self class allInstances do: [:ea | ea shutDown]. Current := self. parameters at: 'ACTIVITY_ID' ifPresent: [ :activityId | OLPCVirtualScreen installIfNeeded. World windowEventHandler: self. DBus sessionBus export: SugarEtoysActivity new at: 'org.laptop.Activity', activityId. Utilities authorName: self presence getOwner nick. ServerDirectory addServer: (SugarDatastoreDirectory mimetype: 'application/x-squeak-project' extension: '.pr') named: SugarLauncher defaultDatastoreDirName. self joinSharedActivity. self isShared ifFalse: [ parameters at: 'OBJECT_ID' ifPresent: [:id | ^self resumeJournalEntry: id]]. self isShared ifTrue: [^self]. ^self welcome: (parameters at: 'URI' ifAbsent: [''])]. self welcome: '' ! ! !SugarLauncher methodsFor: 'commands' stamp: 'bf 6/19/2008 18:54'! quit self leaveSharedActivity. Project current projectParameterAt: #sugarAutoSave put: true; storeOnServerWithNoInteractionThenQuit. ! ! !SugarLauncher methodsFor: 'commands' stamp: 'bf 6/19/2008 18:54'! save Project current projectParameterAt: #sugarAutoSave put: false; storeOnServerWithNoInteraction! ! !SugarLauncher methodsFor: 'commands' stamp: 'bf 6/19/2008 20:40'! share sharedActivity ifNotNil: [^self]. sharedActivity := self presence shareActivity: self activityId with: self bundleId with: (self titleFromProject: Project current) squeakToUtf8 with: Dictionary new. "due to bug 4660 we can't pass properties directly" self enableSharedActivitySignals. sharedActivity setProperties: ({'private' -> false} as: Dictionary).! ! !SugarLauncher methodsFor: 'accessing' stamp: 'bf 6/19/2008 17:04'! buddies ^sharedActivity ifNil: [^#()] ifNotNil: [self getSharedActivityBuddies] ! ! !SugarLauncher methodsFor: 'accessing' stamp: 'bf 6/19/2008 17:43'! dataStore ^DBus sessionBus get: SugarDataStore! ! !SugarLauncher methodsFor: 'accessing' stamp: 'bf 6/19/2008 17:10'! ownerBuddy self isRunningInSugar ifTrue: [^self ownerFromDBus]. "fake" ^SugarBuddyOwner key: '1234' nick: Utilities authorName colors: '#ff0000,#ffff00' ip: '1.2.3.4'! ! !SugarLauncher methodsFor: 'accessing' stamp: 'bf 6/19/2008 16:18'! presence ^DBus sessionBus get: SugarPresence! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 6/19/2008 20:06'! createJournalEntryFor: aProject filename: aFilename mimetype: mimetypeString | properties id | properties := self propertiesFrom: aProject. properties at: 'ctime' put: (properties at: 'mtime'). properties at: 'mime_type' put: mimetypeString. aFilename ifEmpty: [properties at: 'title:text' put: 'Etoys' translated]. id := self dataStore create: properties with: aFilename with: true. ^id! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 6/19/2008 18:21'! findJournalEntries: query properties: propNames "query is either a String or a dictionary, e.g. {'mime_type'->'application/x-squeak-project'}. Answers an array of properties. If propNames is not nil, only the named properties will be returned (which will be more efficient)" ^(self dataStore find: query with: (propNames ifNil: [#()])) first! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 6/19/2008 18:23'! getFile: id "answer a temporary file, will be deleted on #close" | tempName | tempName := self dataStore getFilename: id. tempName isEmptyOrNil ifTrue: [^nil]. ^SugarDatastoreTempFile readOnlyFileNamed: tempName! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 6/19/2008 18:52'! makeJournalEntryFor: aProject filename: aFilename mimetype: mimetypeString | id | (id := parameters at: 'OBJECT_ID' ifAbsent: [nil]) ifNil: [ id := self createJournalEntryFor: aProject filename: aFilename mimetype: mimetypeString. parameters at: 'OBJECT_ID' put: id] ifNotNil: [ (aProject projectParameterAt: #sugarAutoSave ifAbsent: [true]) ifTrue: [self updateJournalEntry: id for: aProject filename: aFilename mimetype: mimetypeString] ifFalse: [self createJournalEntryFor: aProject filename: aFilename mimetype: mimetypeString]]! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 6/19/2008 20:05'! propertiesFrom: aProject | preview autoSave | preview := [ByteArray streamContents: [:s | PNGReadWriter putForm: (aProject thumbnail asFormOfDepth: 16) onStream: s]] ifError: ['']. autoSave := aProject projectParameterAt: #sugarAutoSave ifAbsent: [true]. ^ { 'activity' -> self bundleId. 'activity_id' -> (autoSave ifTrue: [self activityId] 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 6/19/2008 18:55'! resumeJournalEntry: id | props file title project mimetype | props := self dataStore getProperties: id. title := (props at: 'title' ifAbsent: ['untitled' translated]) utf8ToSqueak. mimetype := props at: 'mime_type' ifAbsent: ['']. mimetype isEmpty ifTrue: [^self welcome: '']. mimetype = 'application/x-squeak-project' ifFalse: [ "Do not modify original non-project journal entry when later saving this project" parameters removeKey: 'OBJECT_ID' ifAbsent: []. "reuse drop code" WorldState addDeferredUIMessage: [ ActiveHand lastEvent position: World center. Utilities informUser: 'Opening journal entry' translated, String cr, (title copyReplaceAll: String lf with: String cr) during: [file := self getFile: id]. self handleStream: file mimetype: mimetype titled: title]. ^Project enterNew]. ProjectLoading showProgressBarDuring: [ Display fillWhite;forceToScreen. file := self getFile: id. "load project and close temp file (which will thus be deleted)" project := ProjectLoading loadName: ((title copyReplaceAll: '/' with: '\') contractTo: 64) stream: file fromDirectory: nil withProjectView: nil. file close. project projectParameterAt: #sugarAutoSave put: true. project enter]. ! ! !SugarLauncher methodsFor: 'datastore' stamp: 'bf 6/19/2008 18:40'! updateJournalEntry: id for: aProject filename: aFilename mimetype: mimetypeString "Move aProject saved to aFilename into the datastore, delete aFilename" | properties | properties := self propertiesFrom: aProject. properties at: 'mime_type' put: mimetypeString. self dataStore update: id with: properties with: aFilename with: true.! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 20:49'! enableSharedActivitySignals sharedActivity onBuddyJoinedSend: #handleBuddyJoined: to: self. sharedActivity onBuddyLeftSend: #handleBuddyLeft: to: self. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 20:01'! getSharedActivityBuddies ^sharedActivity getJoinedBuddies collect: [:buddy| SugarBuddy fromDictionary: buddy getProperties]! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 20:59'! handleBuddyJoined: buddyProxy | buddy | buddy := SugarBuddy fromDictionary: buddyProxy getProperties. WorldState addDeferredUIMessage: [self buddyJoined: buddy].! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 20:53'! handleBuddyLeft: buddyProxy | buddy | buddy := SugarBuddy fromDictionary: buddyProxy getProperties. WorldState addDeferredUIMessage: [self buddyLeft: buddy]. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 18:06'! joinSharedActivity "join a shared activity on startup" Utilities informUser: 'Looking for shared activity ...' translated during: [ [sharedActivity := self presence getActivityById: self activityId] ifError: [^sharedActivity := nil]]. Utilities informUser: 'Joining activity ...' translated during: [ [sharedActivity join] ifError: [^sharedActivity := nil] . SugarNavigatorBar current ifNotNilDo: [:bar | bar joinSharedActivity]. self makeBadges. self enableSharedActivitySignals. ]. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 21:13'! leaveSharedActivity sharedActivity ifNotNil: [ sharedActivity leaveAsync. sharedActivity := nil]. ! ! !SugarLauncher methodsFor: 'presence' stamp: 'bf 6/19/2008 20:00'! ownerFromDBus ^SugarBuddy fromDictionary: self presence getOwner getProperties! ! !SugarLauncher methodsFor: 'testing' stamp: 'bf 6/19/2008 17:29'! isRunningInSugar ^self parameters includesKey: 'ACTIVITY_ID'! ! !SugarPresenceActivity methodsFor: 'async' stamp: 'bf 6/19/2008 21:14'! leaveAsync "NOT automatically generated yet" ^self dbusPerformAsync: 'Leave' interface: 'org.laptop.Sugar.Presence.Activity'! ! !SugarPresenceActivity methodsFor: 'signals' stamp: 'bf 6/19/2008 16:51'! onBuddyJoinedSend: aSelector to: anObject "NOT automatically generated, yet ..." self dbusConnection onMatch: (DBusMatch new path: self dbusPath; interface: 'org.laptop.Sugar.Presence.Activity'; member: 'BuddyJoined') do: [:msg | anObject perform: aSelector withArguments: (msg arguments collect: [:each | each fromDBusArgument: self])]! ! !SugarPresenceActivity methodsFor: 'signals' stamp: 'bf 6/19/2008 16:53'! onBuddyLeftSend: aSelector to: anObject "NOT automatically generated, yet ..." self dbusConnection onMatch: (DBusMatch new path: self dbusPath; interface: 'org.laptop.Sugar.Presence.Activity'; member: 'BuddyLeft') do: [:msg | anObject perform: aSelector withArguments: (msg arguments collect: [:each | each fromDBusArgument: self])]! ! !SugarPresenceBuddy methodsFor: 'accessing' stamp: 'bf 6/19/2008 17:33'! nick ^self getProperties at: 'nick'! ! SugarLauncher class removeSelector: #compileDBusMessageRegistry! SugarLauncher removeSelector: #buddyFromPath:! SugarLauncher removeSelector: #dbusMessageRegistry! SugarLauncher removeSelector: #dbusSignalRegistry! SugarLauncher removeSelector: #dispatchDBusMessage:! SugarLauncher removeSelector: #ensureDBus! SugarLauncher removeSelector: #findJournalEntries:! SugarLauncher removeSelector: #getFilename:! SugarLauncher removeSelector: #getProperties:! SugarLauncher removeSelector: #getSharedActivityById:! SugarLauncher removeSelector: #getSharedActivityProperties! SugarLauncher removeSelector: #handleActivitySetActive:! SugarLauncher removeSelector: #handleDBusIntrospect:! SugarLauncher removeSelector: #handleTakeScreenshot:! SugarLauncher removeSelector: #introspect! SugarLauncher removeSelector: #join! SugarLauncher removeSelector: #runDBusService:! SugarLauncher removeSelector: #sendDBusMessage:! SugarLauncher removeSelector: #sendDBusMessage:onSuccess:onError:! SugarLauncher removeSelector: #sendDBusMessage:timeout:! SugarLauncher removeSelector: #setSharedActivityProperties:! SugarLauncher removeSelector: #shareActivityId:bundleId:name:properties:! AbstractLauncher subclass: #SugarLauncher instanceVariableNames: 'sharedActivity' classVariableNames: 'Current UISema' poolDictionaries: '' category: 'Sugar'!