| Home | Trees | Indices | Help |
|
|---|
|
|
1 # This application is released under the GNU General Public License
2 # v3 (or, at your option, any later version). You can find the full
3 # text of the license under http://www.gnu.org/licenses/gpl.txt.
4 # By using, editing and/or distributing this software you agree to
5 # the terms and conditions of this license.
6 # Thank you for using free software!
7
8 # screenlets.session (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com>
9 #
10 # INFO:
11 # This module contains the ScreenletSession-class which handles the lower-level
12 # things like startup, multiple instances and sessions. It should also become
13 # the interface for load/save operations. The ScreenletSession is further
14 # responsible for handling command-line args to the Screenlet and should maybe
15 # offer some convenient way of setting Screenlet-options via commandline (so
16 # one can do "NotesScreenlet --theme_name=green --scale=0.5" and launch the
17 # Note with the given theme and scale)..
18 #
19 #
20 # INFO:
21 # - When a screenlet gets launched:
22 # - the first instance of a screenlet creates the Session-object (within the
23 # __main__-code)
24 # - the session object investigates the config-dir for the given Screenlet
25 # and restores available instances
26 # - else (if no instance was found) it simply creates a new instance of the
27 # given screenlet and runs its mainloop
28 # - the --session argument allows setting the name of the session that will be
29 # used by the Screenlet (to allow multiple configs for one Screenlet)
30 #
31 # TODO:
32 # - set attributes via commandline??
33 #
34
35 import os
36 import glob
37 import random
38 from xdg import BaseDirectory
39
40 import backend # import screenlets.backend module
41 import services
42 import utils
43
44 import dbus # TEMPORARY!! only needed for workaround
45 from stat import S_IRWXU, S_IRWXG, S_IRWXO
46 import gettext
47 import screenlets
48 gettext.textdomain('screenlets')
49 gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX + '/share/locale')
50
53
54
55 # temporary path for saving files for opened screenlets
56 TMP_DIR = '/tmp/screenlets'
57 TMP_FILE = 'screenlets.' + os.environ['USER'] + '.running'
58
59
61 """The ScreenletSession manages instances of a Screenlet and handles
62 saving/restoring options. Each Screenlet contains a reference to its
63 session. Multiple instances of the same Screenlet share the same
64 session-object."""
65
66 # constructor
68 object.__init__(self)
69 # check type
70 if not screenlet_classobj.__name__.endswith('Screenlet'):
71 # TODO: also check for correct type (Screenlet-subclass)!!
72 raise Exception("ScreenletSession.__init__ has to be called with a valid Screenlet-classobject as first argument!")
73 # init props
74 self.name = name
75 self.screenlet = screenlet_classobj
76 self.instances = []
77 self.tempfile = TMP_DIR + '/' + TMP_FILE
78 # check sys.args for "--session"-argument and override name, if set
79 self.__parse_commandline()
80 # set session path (and create dir-tree if not existent)
81 p = screenlet_classobj.__name__[:-9] + '/' + self.name + '/'
82 self.path = BaseDirectory.load_first_config('Screenlets/' + p)
83 if self.path == None:
84 self.path = BaseDirectory.save_config_path('Screenlets/' + p)
85 if self.path == None: self.path = (os.environ['HOME'] + '.config/Screenlets/' + p)
86 if self.path:
87 if backend_type == 'caching':
88 self.backend = backend.CachingBackend(path=self.path)
89 elif backend_type == 'gconf':
90 self.backend = backend.GconfBackend()
91 else:
92 # no config-dir? use dummy-backend and note about problem
93 self.backend = backend.ScreenletsBackend()
94 print "Unable to init backend - settings will not be saved!"
95 # WORKAROUND: connect to daemon (ideally the daemon should watch the
96 # tmpfile for changes!!)
97 #check for daemon
98 proc = os.popen("""ps axo "%p,%a" | grep "python.*screenlets-daemon.py" | grep -v grep|cut -d',' -f1""").read()
99
100 procs = proc.split('\n')
101 if len(procs) <= 1:
102 os.system('python -u ' + screenlets.INSTALL_PREFIX + '/share/screenlets-manager/screenlets-daemon.py &')
103 print 'No Daemon, Launching Daemon'
104 self.connect_daemon()
105
107 """Connect to org.screenlets.ScreenletsDaemon."""
108 self.daemon_iface = None
109 bus = dbus.SessionBus()
110 if bus:
111 try:
112 proxy_obj = bus.get_object(screenlets.DAEMON_BUS, screenlets.DAEMON_PATH)
113 if proxy_obj:
114 self.daemon_iface = dbus.Interface(proxy_obj, screenlets.DAEMON_IFACE)
115 except Exception, ex:
116 print "Error in screenlets.session.connect_daemon: %s" % ex
117
119 """Create a new instance with ID 'id' and add it to this session. The
120 function returns either the new Screenlet-instance or None."""
121 # if id is none or already exists
122 if id==None or id=='' or self.get_instance_by_id(id) != None:
123 print "ID is unset or already in use - creating new one!"
124 id = self.__get_next_id()
125 dirlst = glob.glob(self.path + '*')
126 tdlen = len(self.path)
127 for filename in dirlst:
128 filename = filename[tdlen:] # strip path from filename
129 print 'Loaded config from: %s' % filename
130 if filename.endswith(id + '.ini'):
131 # create new instance
132 sl = self.create_instance(id=filename[:-4], enable_saving=False)
133 if sl:
134 # set options for the screenlet
135 print "Set options in %s" % sl.__name__
136 #self.__restore_options_from_file (sl, self.path + filename)
137 self.__restore_options_from_backend(sl, self.path+filename)
138 sl.enable_saving(True)
139 # and call init handler
140 sl.finish_loading()
141 return sl
142 sl = self.screenlet(id=id, session=self, **keyword_args)
143 if sl:
144 self.instances.append(sl) # add screenlet to session
145 # and cause initial save to store INI-file in session dir
146 sl.x = sl.x
147 return sl
148 return None
149
151 """Delete the given instance with ID 'id' and remove its session file.
152 When the last instance within the session is removed, the session dir
153 is completely removed."""
154 sl = self.get_instance_by_id(id)
155 if sl:
156 # remove instance from session
157 self.instances.remove(sl)
158 # remove session file
159 try:
160 self.backend.delete_instance(id)
161 except Exception:
162 print "Failed to remove INI-file for instance (not critical)."
163 # if this was the last instance
164 if len(self.instances) == 0:
165 # maybe show confirmation popup?
166 print "Removing last instance from session"
167 # TODO: remove whole session directory
168 print "TODO: remove self.path: %s" % self.path
169 try:
170 os.rmdir(self.path)
171 except:
172 print "Failed to remove session dir '%s' - not empty?" % self.name
173 # ...
174 # quit gtk on closing screenlet
175 sl.quit_on_close = True
176 else:
177 print "Removing instance from session but staying alive"
178 sl.quit_on_close = False
179 # delete screenlet instance
180 sl.close()
181 del sl
182 return True
183 return False
184
186 """Return the instance with the given id from within this session."""
187 for inst in self.instances:
188 if inst.id == id:
189 return inst
190 return None
191
193 """quit the given instance with ID 'id'"""
194
195 sl = self.get_instance_by_id(id)
196 if sl:
197 print self.instances
198 # remove instance from session
199
200
201 if len(self.instances) == 1:
202 sl.quit_on_close = True
203 else:
204 print "Removing instance from session but staying alive"
205 sl.quit_on_close = False
206 self.backend.flush()
207 sl.close()
208 self.instances.remove(sl)
209
210 # remove session file
211 return True
212 return False
213
214
216 """Start a new session (or restore an existing session) for the
217 current Screenlet-class. Creates a new instance when none is found.
218 Returns True if everything worked well, else False."""
219 # check for a running instance first and use dbus-call to add
220 # a new instance in that case
221 #sln = self.screenlet.get_short_name()
222 sln = self.screenlet.__name__[:-9]
223 running = utils.list_running_screenlets()
224 if running and running.count(self.screenlet.__name__) > 0:
225 #if services.service_is_running(sln):
226 print "Found a running session of %s, adding new instance by service." % sln
227 srvc = services.get_service_by_name(sln)
228 if srvc:
229 print "Adding new instance through: %s" % str(srvc)
230 srvc.add('')
231 return False
232 # ok, we have a new session running - indicate that to the system
233 self.__register_screenlet()
234 # check for existing entries in the session with the given name
235 print "Loading instances in: %s" % self.path
236 if self.__load_instances():
237 # restored existing entries?
238 print "Restored instances from session '%s' ..." % self.name
239 # call mainloop of first instance (starts application)
240 #self.instances[0].main()
241 self.__run_session(self.instances[0])
242 else:
243 # create new first instance
244 print 'No instance(s) found in session-path, creating new one.'
245 sl = self.screenlet(session=self, id=self.__get_next_id())
246 if sl:
247 # add screenlet to session
248 self.instances.append(sl)
249 # now cause a save of the options to initially create the
250 # INI-file for this instance
251 self.backend.save_option(sl.id, 'x', sl.x)
252 # call on_init-handler
253 sl.finish_loading()
254 # call mainloop and give control to Screenlet
255 #sl.main()
256 self.__run_session(sl)
257 else:
258 print 'Failed creating instance of: %s' % self.classobj.__name__
259 # remove us from the running screenlets
260 self.__unregister_screenlet()
261 return False
262 # all went well
263 return True
264
266 """Create new entry for this session in the global TMP_FILE."""
267
268 # if tempfile not exists, create it
269 if not self.__create_tempdir():
270 return False # error already returned
271
272 # if screenlet not already added
273 running = utils.list_running_screenlets()
274 if running == None : running = []
275 if running.count(self.screenlet.__name__) == 0:
276 # open temp file for appending data
277 try:
278 f = open(self.tempfile, 'a') # No need to create a empty file , append will do just fine
279 except IOError, e:
280 print "Unable to open %s" % self.tempfile
281 return False
282 else:
283 print "Creating new entry for %s in %s" % (self.screenlet.__name__, self.tempfile)
284 f.write(self.screenlet.__name__ + '\n')
285 f.close()
286 else: print "Screenlet has already been added to %s" % self.tempfile
287 # WORKAROUND: for now we manually add this to the daemon,
288 # ideally the daemon should watch the tmpdir for changes
289 if self.daemon_iface:
290 self.daemon_iface.register_screenlet(self.screenlet.__name__)
291
293 """Create the global temporary file for saving screenlets. The file is
294 used for indicating which screnlets are currently running."""
295
296 # check for existence of TMP_DIR and create it if missing
297 if not os.path.isdir(TMP_DIR):
298 try:
299 if os.path.exists(TMP_DIR):
300 # something exists, but is not a directory
301 os.remove(TMP_DIR)
302
303 print "No global tempdir found, creating new one."
304 os.mkdir(TMP_DIR)
305 # make the tmp directory accessible for all users
306
307 os.chmod(TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO)
308 print 'Temp directory %s created.' % TMP_DIR
309 except OSError, e:
310 print 'Error: Unable to create temp directory %s - screenlets-manager will not work properly.' % TMP_DIR
311 print "Error was: %s"%e
312 return False
313 return True
314
315
317 """Delete this session's entry from the gloabl tempfile (and delete the
318 entire file if no more running screenlets are set."""
319 if not name:
320 name = self.screenlet.__name__
321 # WORKAROUND: for now we manually unregister from the daemon,
322 # ideally the daemon should watch the tmpfile for changes
323 if self.daemon_iface:
324 try:
325 self.daemon_iface.unregister_screenlet(name)
326 except Exception, ex:
327 print "Failed to unregister from daemon: %s" % ex
328 # /WORKAROUND
329 # get running screenlets
330 running = utils.list_running_screenlets()
331 if running and len(running) > 0:
332 pass#print "Removing entry for %s from global tempfile %s" % (name, self.tempfile)
333 try:
334 running.remove(name)
335 except:
336 # not found, so ok
337 print "Entry not found. Will (obviously) not be removed."
338 return True
339 # still running screenlets?
340 if running and len(running) > 0:
341 # re-save new list of running screenlets
342 f = open(self.tempfile, 'w')
343 if f:
344 for r in running:
345 f.write(r + '\n')
346 f.close()
347 return True
348 else:
349 print "Error global tempfile not found. Some error before?"
350 return False
351 else:
352 print 'No more screenlets running.'
353 self.__delete_tempfile(name)
354 else:
355 print 'No screenlets running?'
356 return False
357
359 """Delete the tempfile for this session."""
360 if self.tempfile and os.path.isfile(self.tempfile):
361 print "Deleting global tempfile %s" % self.tempfile
362 try:
363 os.remove(self.tempfile)
364 return True
365 except:
366 print "Error: Failed to delete global tempfile"
367 return False
368
370 """Get the next ID for an instance of the assigned Screenlet."""
371 num = 1
372 sln = self.screenlet.__name__[:-9]
373 id = sln + str(num)
374 while self.get_instance_by_id(id) != None:
375 id = sln + str(num)
376 num += 1
377 return id
378
380 """Check for existing instances in the current session, create them
381 and store them into self.instances if any are found. Returns True if
382 at least one instance was found, else False."""
383 dirlst = glob.glob(self.path + '*')
384 tdlen = len(self.path)
385 for filename in dirlst:
386 filename = filename[tdlen:] # strip path from filename
387 print 'Loaded config from: %s' % filename
388 if filename.endswith('.ini'):
389 # create new instance
390 sl = self.create_instance(id=filename[:-4], enable_saving=False)
391 if sl:
392 # set options for the screenlet
393 print "Set options in %s" % sl.__name__
394 #self.__restore_options_from_file (sl, self.path + filename)
395 self.__restore_options_from_backend(sl, self.path+filename)
396 sl.enable_saving(True)
397 # and call init handler
398 sl.finish_loading()
399 else:
400 print "Failed to create instance of '%s'!" % filename[:-4]
401 # if instances were found, return True, else False
402 if len(self.instances) > 0:
403 return True
404 return False
405
406 # replacement for above function
408 """Restore and apply a screenlet's options from the backend."""
409 # disable the canvas-updates in the screenlet
410 screenlet.disable_updates = True
411 # get options for SL from backend
412 opts = self.backend.load_instance(screenlet.id)
413 if opts:
414 for o in opts:
415 # get the attribute's Option-object from Screenlet
416 opt = screenlet.get_option_by_name(o)
417 # NOTE: set attribute in Screenlet by calling the
418 # on_import-function for the Option (to import
419 # the value as the required type)
420 if opt:
421 setattr(screenlet, opt.name, opt.on_import(opts[o]))
422 # re-enable updates and call redraw/reshape
423 screenlet.disable_updates = False
424 screenlet.redraw_canvas()
425 screenlet.update_shape()
426
428 """Run the session by calling the main handler of the given Screenlet-
429 instance. Handles sigkill (?) and keyboard interrupts."""
430 # add sigkill-handler
431 import signal
432 def on_kill(*args):
433 #print "Screenlet has been killed. TODO: make this an event"
434 pass
435 signal.signal(signal.SIGTERM, on_kill)
436 # set name of tempfile for later (else its missing after kill)
437 tempfile = self.screenlet.__name__
438 # start
439 try:
440 # start mainloop of screenlet
441 main_instance.main()
442 except KeyboardInterrupt:
443 # notify when daemon is closed
444 self.backend.flush()
445 print "Screenlet '%s' has been interrupted by keyboard. TODO: make this an event" % self.screenlet.__name__
446 except Exception, ex:
447 print "Exception in ScreenletSession: " + ex
448 # finally delete the tempfile
449 self.__unregister_screenlet(name=tempfile)
450
452 """Check commandline args for "--session" argument and set session
453 name if found. Runs only once during __init__.
454 TODO: handle more arguments and maybe allow setting options by
455 commandline"""
456 import sys
457 for arg in sys.argv[1:]:
458 # name of session?
459 if arg.startswith('--session=') and len(arg)>10:
460 self.name = arg[10:]
461
462
463
465 """A very simple utility-function to easily create/start a new session."""
466
467 if threading:
468 import gtk
469 gtk.gdk.threads_init()
470 session = ScreenletSession(classobj, backend_type=backend)
471 session.start()
472
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Feb 28 23:21:25 2011 | http://epydoc.sourceforge.net |