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