| 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
98
99 # NOTE: daemon will be started by bus.get_object anyway!!!
100 #check for daemon
101 #proc = os.popen("""ps axo "%p,%a" | grep "python.*screenlets-daemon.py" | grep -v grep|cut -d',' -f1""").read()
102
103 #procs = proc.split('\n')
104 #if len(procs) <= 1:
105 # os.system('python -u ' + screenlets.INSTALL_PREFIX + '/share/screenlets-manager/screenlets-daemon.py &')
106 # print 'No Daemon, Launching Daemon'
107 self.connect_daemon()
108
110 """Connect to org.screenlets.ScreenletsDaemon."""
111 self.daemon_iface = None
112 bus = dbus.SessionBus()
113 if bus:
114 try:
115 proxy_obj = bus.get_object(screenlets.DAEMON_BUS, screenlets.DAEMON_PATH)
116 if proxy_obj:
117 self.daemon_iface = dbus.Interface(proxy_obj, screenlets.DAEMON_IFACE)
118 except Exception, ex:
119 print "Error in screenlets.session.connect_daemon: %s" % ex
120
122 """Create a new instance with ID 'id' and add it to this session. The
123 function returns either the new Screenlet-instance or None."""
124 # if id is none or already exists
125 if id==None or id=='' or self.get_instance_by_id(id) != None:
126 print "ID is unset or already in use - creating new one!"
127 id = self.__get_next_id()
128 dirlst = glob.glob(self.path + '*')
129 tdlen = len(self.path)
130 for filename in dirlst:
131 filename = filename[tdlen:] # strip path from filename
132 print 'Loaded config from: %s' % filename
133 if filename.endswith(id + '.ini'):
134 # create new instance
135 sl = self.create_instance(id=filename[:-4], enable_saving=False)
136 if sl:
137 # set options for the screenlet
138 print "Set options in %s" % sl.__name__
139 #self.__restore_options_from_file (sl, self.path + filename)
140 self.__restore_options_from_backend(sl, self.path+filename)
141 sl.enable_saving(True)
142 # and call init handler
143 sl.finish_loading()
144 return sl
145 sl = self.screenlet(id=id, session=self, **keyword_args)
146 if sl:
147 self.instances.append(sl) # add screenlet to session
148 # and cause initial save to store INI-file in session dir
149 sl.x = sl.x
150 return sl
151 return None
152
154 """Delete the given instance with ID 'id' and remove its session file.
155 When the last instance within the session is removed, the session dir
156 is completely removed."""
157 sl = self.get_instance_by_id(id)
158 if sl:
159 # remove instance from session
160 self.instances.remove(sl)
161 # remove session file
162 try:
163 self.backend.delete_instance(id)
164 except Exception:
165 print "Failed to remove INI-file for instance (not critical)."
166 # if this was the last instance
167 if len(self.instances) == 0:
168 # maybe show confirmation popup?
169 print "Removing last instance from session"
170 # TODO: remove whole session directory
171 print "TODO: remove self.path: %s" % self.path
172 try:
173 os.rmdir(self.path)
174 except:
175 print "Failed to remove session dir '%s' - not empty?" % self.name
176 # ...
177 # quit gtk on closing screenlet
178 sl.quit_on_close = True
179 else:
180 print "Removing instance from session but staying alive"
181 sl.quit_on_close = False
182 # delete screenlet instance
183 sl.close()
184 del sl
185 return True
186 return False
187
189 """Return the instance with the given id from within this session."""
190 for inst in self.instances:
191 if inst.id == id:
192 return inst
193 return None
194
196 """quit the given instance with ID 'id'"""
197
198 sl = self.get_instance_by_id(id)
199 if sl:
200 print self.instances
201 # remove instance from session
202
203
204 if len(self.instances) == 1:
205 sl.quit_on_close = True
206 else:
207 print "Removing instance from session but staying alive"
208 sl.quit_on_close = False
209 self.backend.flush()
210 sl.close()
211 self.instances.remove(sl)
212
213 # remove session file
214 return True
215 return False
216
217
219 """Start a new session (or restore an existing session) for the
220 current Screenlet-class. Creates a new instance when none is found.
221 Returns True if everything worked well, else False."""
222 # check for a running instance first and use dbus-call to add
223 # a new instance in that case
224 #sln = self.screenlet.get_short_name()
225 sln = self.screenlet.__name__[:-9]
226 running = utils.list_running_screenlets()
227 if running and running.count(self.screenlet.__name__) > 0:
228 #if services.service_is_running(sln):
229 print "Found a running session of %s, adding new instance by service." % sln
230 srvc = services.get_service_by_name(sln)
231 if srvc:
232 print "Adding new instance through: %s" % str(srvc)
233 srvc.add('')
234 return False
235 # ok, we have a new session running - indicate that to the system
236 self.__register_screenlet()
237 # check for existing entries in the session with the given name
238 print "Loading instances in: %s" % self.path
239 if self.__load_instances():
240 # restored existing entries?
241 print "Restored instances from session '%s' ..." % self.name
242 # call mainloop of first instance (starts application)
243 #self.instances[0].main()
244 self.__run_session(self.instances[0])
245 else:
246 # create new first instance
247 print 'No instance(s) found in session-path, creating new one.'
248 sl = self.screenlet(session=self, id=self.__get_next_id())
249 if sl:
250 # add screenlet to session
251 self.instances.append(sl)
252 # now cause a save of the options to initially create the
253 # INI-file for this instance
254 self.backend.save_option(sl.id, 'x', sl.x)
255 # call on_init-handler
256 sl.finish_loading()
257 # call mainloop and give control to Screenlet
258 #sl.main()
259 self.__run_session(sl)
260 else:
261 print 'Failed creating instance of: %s' % self.classobj.__name__
262 # remove us from the running screenlets
263 self.__unregister_screenlet()
264 return False
265 # all went well
266 return True
267
269 """Create new entry for this session in the global TMP_FILE."""
270
271 # if tempfile not exists, create it
272 if not self.__create_tempdir():
273 return False # error already returned
274
275 # if screenlet not already added
276 running = utils.list_running_screenlets()
277 if running == None : running = []
278 if running.count(self.screenlet.__name__) == 0:
279 # open temp file for appending data
280 try:
281 f = open(self.tempfile, 'a') # No need to create a empty file , append will do just fine
282 except IOError, e:
283 print "Unable to open %s" % self.tempfile
284 return False
285 else:
286 print "Creating new entry for %s in %s" % (self.screenlet.__name__, self.tempfile)
287 f.write(self.screenlet.__name__ + '\n')
288 f.close()
289 else: print "Screenlet has already been added to %s" % self.tempfile
290 # WORKAROUND: for now we manually add this to the daemon,
291 # ideally the daemon should watch the tmpdir for changes
292 if self.daemon_iface:
293 self.daemon_iface.register_screenlet(self.screenlet.__name__)
294
296 """Create the global temporary file for saving screenlets. The file is
297 used for indicating which screnlets are currently running."""
298
299 # check for existence of TMP_DIR and create it if missing
300 if not os.path.isdir(TMP_DIR):
301 try:
302 if os.path.exists(TMP_DIR):
303 # something exists, but is not a directory
304 os.remove(TMP_DIR)
305
306 print "No global tempdir found, creating new one."
307 os.mkdir(TMP_DIR)
308 # make the tmp directory accessible for all users
309
310 os.chmod(TMP_DIR, S_IRWXU | S_IRWXG | S_IRWXO)
311 print 'Temp directory %s created.' % TMP_DIR
312 except OSError, e:
313 print 'Error: Unable to create temp directory %s - screenlets-manager will not work properly.' % TMP_DIR
314 print "Error was: %s"%e
315 return False
316 return True
317
318
320 """Delete this session's entry from the gloabl tempfile (and delete the
321 entire file if no more running screenlets are set."""
322 if not name:
323 name = self.screenlet.__name__
324 # WORKAROUND: for now we manually unregister from the daemon,
325 # ideally the daemon should watch the tmpfile for changes
326 if self.daemon_iface:
327 try:
328 self.daemon_iface.unregister_screenlet(name)
329 except Exception, ex:
330 print "Failed to unregister from daemon: %s" % ex
331 # /WORKAROUND
332 # get running screenlets
333 running = utils.list_running_screenlets()
334 if running and len(running) > 0:
335 pass#print "Removing entry for %s from global tempfile %s" % (name, self.tempfile)
336 try:
337 running.remove(name)
338 except:
339 # not found, so ok
340 print "Entry not found. Will (obviously) not be removed."
341 return True
342 # still running screenlets?
343 if running and len(running) > 0:
344 # re-save new list of running screenlets
345 f = open(self.tempfile, 'w')
346 if f:
347 for r in running:
348 f.write(r + '\n')
349 f.close()
350 return True
351 else:
352 print "Error global tempfile not found. Some error before?"
353 return False
354 else:
355 print 'No more screenlets running.'
356 self.__delete_tempfile(name)
357 else:
358 print 'No screenlets running?'
359 return False
360
362 """Delete the tempfile for this session."""
363 if self.tempfile and os.path.isfile(self.tempfile):
364 print "Deleting global tempfile %s" % self.tempfile
365 try:
366 os.remove(self.tempfile)
367 return True
368 except:
369 print "Error: Failed to delete global tempfile"
370 return False
371
373 """Get the next ID for an instance of the assigned Screenlet."""
374 num = 1
375 sln = self.screenlet.__name__[:-9]
376 id = sln + str(num)
377 while self.get_instance_by_id(id) != None:
378 id = sln + str(num)
379 num += 1
380 return id
381
383 """Check for existing instances in the current session, create them
384 and store them into self.instances if any are found. Returns True if
385 at least one instance was found, else False."""
386 dirlst = glob.glob(self.path + '*')
387 tdlen = len(self.path)
388 for filename in dirlst:
389 filename = filename[tdlen:] # strip path from filename
390 print 'Loaded config from: %s' % filename
391 if filename.endswith('.ini'):
392 # create new instance
393 sl = self.create_instance(id=filename[:-4], enable_saving=False)
394 if sl:
395 # set options for the screenlet
396 print "Set options in %s" % sl.__name__
397 #self.__restore_options_from_file (sl, self.path + filename)
398 self.__restore_options_from_backend(sl, self.path+filename)
399 sl.enable_saving(True)
400 # and call init handler
401 sl.finish_loading()
402 else:
403 print "Failed to create instance of '%s'!" % filename[:-4]
404 # if instances were found, return True, else False
405 if len(self.instances) > 0:
406 return True
407 return False
408
409 # replacement for above function
411 """Restore and apply a screenlet's options from the backend."""
412 # disable the canvas-updates in the screenlet
413 screenlet.disable_updates = True
414 # get options for SL from backend
415 opts = self.backend.load_instance(screenlet.id)
416 if opts:
417 for o in opts:
418 # get the attribute's Option-object from Screenlet
419 opt = screenlet.get_option_by_name(o)
420 # NOTE: set attribute in Screenlet by calling the
421 # on_import-function for the Option (to import
422 # the value as the required type)
423 if opt:
424 setattr(screenlet, opt.name, opt.on_import(opts[o]))
425 # re-enable updates and call redraw/reshape
426 screenlet.disable_updates = False
427 screenlet.redraw_canvas()
428 screenlet.update_shape()
429
431 """Run the session by calling the main handler of the given Screenlet-
432 instance. Handles sigkill (?) and keyboard interrupts."""
433 # add sigkill-handler
434 import signal
435 def on_kill(*args):
436 #print "Screenlet has been killed. TODO: make this an event"
437 pass
438 signal.signal(signal.SIGTERM, on_kill)
439 # set name of tempfile for later (else its missing after kill)
440 tempfile = self.screenlet.__name__
441 # start
442 try:
443 # start mainloop of screenlet
444 main_instance.main()
445 except KeyboardInterrupt:
446 # notify when daemon is closed
447 self.backend.flush()
448 print "Screenlet '%s' has been interrupted by keyboard. TODO: make this an event" % self.screenlet.__name__
449 except Exception, ex:
450 print "Exception in ScreenletSession: " + ex
451 # finally delete the tempfile
452 self.__unregister_screenlet(name=tempfile)
453
455 """Check commandline args for "--session" argument and set session
456 name if found. Runs only once during __init__.
457 TODO: handle more arguments and maybe allow setting options by
458 commandline"""
459 import sys
460 for arg in sys.argv[1:]:
461 # name of session?
462 if arg.startswith('--session=') and len(arg)>10:
463 self.name = arg[10:]
464
465
466
468 """A very simple utility-function to easily create/start a new session."""
469
470 if threading:
471 import gtk
472 gtk.gdk.threads_init()
473 session = ScreenletSession(classobj, backend_type=backend)
474 session.start()
475
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 6 10:56:37 2011 | http://epydoc.sourceforge.net |