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 os.path.isfile(self.tempfile) and not self.__create_tempfile(): 266 print _('Error: Unable to create temp entry - screenlets-manager will not work properly.') 267 return False 268 # open temp file for appending data 269 f = open(self.tempfile, 'a') 270 if f: 271 # if screenlet not already added 272 running = utils.list_running_screenlets() 273 if running.count(self.screenlet.__name__) == 0: 274 print _("Creating new entry for %s in %s") % (self.screenlet.__name__, self.tempfile) 275 f.write(self.screenlet.__name__ + '\n') 276 f.close() 277 # WORKAROUND: for now we manually add this to the daemon, 278 # ideally the daemon should watch the tmpdir for changes 279 if self.daemon_iface: 280 self.daemon_iface.register_screenlet(self.screenlet.__name__)
281
282 - def __create_tempfile (self):
283 """Create the global temporary file for saving screenlets. The file is 284 used for indicating which screnlets are currently running.""" 285 # check for existence of TMP_DIR and create it if missing 286 if not os.path.isdir(TMP_DIR): 287 print _("No global tempfile found, creating new one.") 288 os.mkdir(TMP_DIR) 289 if not os.path.isdir(TMP_DIR): 290 print _('Error: Unable to create temp directory %s - screenlets-manager will not work properly.') % TMP_DIR 291 return False 292 else: 293 # create entry in temp dir 294 f = open(self.tempfile, 'w') 295 f.close() 296 return True
297
298 - def __unregister_screenlet (self, name=None):
299 """Delete this session's entry from the gloabl tempfile (and delete the 300 entire file if no more running screenlets are set.""" 301 if not name: 302 name = self.screenlet.__name__ 303 # WORKAROUND: for now we manually unregister from the daemon, 304 # ideally the daemon should watch the tmpfile for changes 305 if self.daemon_iface: 306 try: 307 self.daemon_iface.unregister_screenlet(name) 308 except Exception, ex: 309 print _("Failed to unregister from daemon: %s") % ex 310 # /WORKAROUND 311 # get running screenlets 312 running = utils.list_running_screenlets() 313 if running and len(running) > 0: 314 print _("Removing entry for %s from global tempfile %s") % (name, self.tempfile) 315 try: 316 running.remove(name) 317 except: 318 # not found, so ok 319 print _("Entry not found. Will (obviously) not be removed.") 320 return True 321 # still running screenlets? 322 if running and len(running) > 0: 323 # re-save new list of running screenlets 324 f = open(self.tempfile, 'w') 325 if f: 326 for r in running: 327 f.write(r + '\n') 328 f.close() 329 return True 330 else: 331 print _("Error global tempfile not found. Some error before?") 332 return False 333 else: 334 print _('No more screenlets running.') 335 self.__delete_tempfile(name) 336 else: 337 print _('No screenlets running?') 338 return False
339
340 - def __delete_tempfile (self, name=None):
341 """Delete the tempfile for this session.""" 342 if self.tempfile and os.path.isfile(self.tempfile): 343 print _("Deleting global tempfile %s") % self.tempfile 344 try: 345 os.remove(self.tempfile) 346 return True 347 except: 348 print _("Error: Failed to delete global tempfile") 349 return False
350
351 - def __get_next_id (self):
352 """Get the next ID for an instance of the assigned Screenlet.""" 353 num = 1 354 sln = self.screenlet.__name__[:-9] 355 id = sln + str(num) 356 while self.get_instance_by_id(id) != None: 357 id = sln + str(num) 358 num += 1 359 return id
360
361 - def __load_instances (self):
362 """Check for existing instances in the current session, create them 363 and store them into self.instances if any are found. Returns True if 364 at least one instance was found, else False.""" 365 dirlst = glob.glob(self.path + '*') 366 tdlen = len(self.path) 367 for filename in dirlst: 368 filename = filename[tdlen:] # strip path from filename 369 print _('File: %s') % filename 370 if filename.endswith('.ini'): 371 # create new instance 372 sl = self.create_instance(id=filename[:-4], enable_saving=False) 373 if sl: 374 # set options for the screenlet 375 print _("Set options in %s") % sl.__name__ 376 #self.__restore_options_from_file (sl, self.path + filename) 377 self.__restore_options_from_backend(sl, self.path+filename) 378 sl.enable_saving(True) 379 # and call init handler 380 sl.finish_loading() 381 else: 382 print _("Failed to create instance of '%s'!") % filename[:-4] 383 # if instances were found, return True, else False 384 if len(self.instances) > 0: 385 return True 386 return False
387 388 # replacement for above function
389 - def __restore_options_from_backend (self, screenlet, filename):
390 """Restore and apply a screenlet's options from the backend.""" 391 # disable the canvas-updates in the screenlet 392 screenlet.disable_updates = True 393 # get options for SL from backend 394 opts = self.backend.load_instance(screenlet.id) 395 if opts: 396 for o in opts: 397 # get the attribute's Option-object from Screenlet 398 opt = screenlet.get_option_by_name(o) 399 # NOTE: set attribute in Screenlet by calling the 400 # on_import-function for the Option (to import 401 # the value as the required type) 402 if opt: 403 setattr(screenlet, opt.name, opt.on_import(opts[o])) 404 # re-enable updates and call redraw/reshape 405 screenlet.disable_updates = False 406 screenlet.redraw_canvas() 407 screenlet.update_shape()
408
409 - def __run_session (self, main_instance):
410 """Run the session by calling the main handler of the given Screenlet- 411 instance. Handles sigkill (?) and keyboard interrupts.""" 412 # add sigkill-handler 413 import signal 414 def on_kill(): 415 print _("Screenlet has been killed. TODO: make this an event")
416 signal.signal(signal.SIGTERM, on_kill) 417 # set name of tempfile for later (else its missing after kill) 418 tempfile = self.screenlet.__name__ 419 # start 420 try: 421 # start mainloop of screenlet 422 main_instance.main() 423 except KeyboardInterrupt: 424 # notify when daemon is closed 425 self.backend.flush() 426 print _("Screenlet '%s' has been interrupted by keyboard. TODO: make this an event") % self.screenlet.__name__ 427 except Exception, ex: 428 print _("Exception in ScreenletSession: ") + ex 429 # finally delete the tempfile 430 self.__unregister_screenlet(name=tempfile)
431
432 - def __parse_commandline (self):
433 """Check commandline args for "--session" argument and set session 434 name if found. Runs only once during __init__. 435 TODO: handle more arguments and maybe allow setting options by 436 commandline""" 437 import sys 438 for arg in sys.argv[1:]: 439 # name of session? 440 if arg.startswith('--session=') and len(arg)>10: 441 self.name = arg[10:]
442 443 444
445 -def create_session (classobj, backend='caching', threading=False):
446 """A very simple utility-function to easily create/start a new session.""" 447 if threading: 448 import gtk 449 gtk.gdk.threads_init() 450 session = ScreenletSession(classobj, backend_type=backend) 451 session.start()
452