Package screenlets :: Module session
[hide private]
[frames] | no frames]

Source Code for Module screenlets.session

  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   
51 -def _(s):
52 return gettext.gettext(s)
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
60 -class ScreenletSession (object):
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
67 - def __init__ (self, screenlet_classobj, backend_type='caching', name='default'):
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
99 - def connect_daemon (self):
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
114 - def create_instance (self, id=None, **keyword_args):
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
147 - def delete_instance (self, id):
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
182 - def get_instance_by_id (self, id):
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
189 - def quit_instance (self, id):
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
212 - def start (self):
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
262 - def __register_screenlet (self):
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
288 - def __create_tempdir (self):
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
313 - def __unregister_screenlet (self, name=None):
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
355 - def __delete_tempfile (self, name=None):
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
366 - def __get_next_id (self):
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
376 - def __load_instances (self):
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
404 - def __restore_options_from_backend (self, screenlet, filename):
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
424 - def __run_session (self, main_instance):
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
447 - def __parse_commandline (self):
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
460 -def create_session (classobj, backend='caching', threading=False):
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