[postr: 1/8] Add PyUnique code



commit 863a97bede56868a7b862782f9a6351479cfbe98
Author: Karl Mikaelsson <derfian lysator liu se>
Date:   Wed Feb 3 08:59:47 2010 +0100

    Add PyUnique code

 src/PyUnique.py |  200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 200 insertions(+), 0 deletions(-)
---
diff --git a/src/PyUnique.py b/src/PyUnique.py
new file mode 100644
index 0000000..e182671
--- /dev/null
+++ b/src/PyUnique.py
@@ -0,0 +1,200 @@
+# -*- encoding: utf-8; mode: python -*-
+# PyUnique, a pure python reimplementation of unique, a
+# single-instance application library.
+#
+# Copyright © 2010 Karl Mikaelsson <derfian lysator liu se>
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+# St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+import gobject
+import dbus
+import dbus.service
+from dbus.mainloop.glib import DBusGMainLoop
+from gtk import gdk
+import logging
+
+# Set the glib main loop as the default main loop for dbus
+#
+DBusGMainLoop(set_as_default=True)
+
+# Default Unique response codes
+#
+RESPONSE_INVALID = 0
+RESPONSE_OK = 1
+RESPONSE_CANCEL = 2
+RESPONSE_FAIL = 3
+RESPONSE_PASSTHROUGH = 4
+
+class UniqueException(Exception):
+    """ Base class for Unique exceptions """
+    pass
+
+class UniqueBadCommand(UniqueException):
+    """ A bad command id was given """
+    pass
+
+class UniqueNotRunning(UniqueException):
+    """ An instance of UniqueApp isn't running """
+    pass
+
+class UniqueDBusObject(dbus.service.Object):
+    """ Listener object on the dbus session bus. """
+    def __init__(self, bus, path, app):
+        dbus.service.Object.__init__(self, bus, path)
+        self.app = app
+
+    @dbus.service.method("org.gtk.PyUniqueApp", in_signature='is', out_signature='s')
+    def SendMessage(self, command, data):
+        self.app.emit('message-received', command, data)
+        return "OK"
+    
+class UniqueApp(gobject.GObject):
+    """ Base class for every single instance application."""
+
+    __gproperties__ = {
+
+        'is-running': (gobject.TYPE_BOOLEAN, 'is-running', 'is-running',
+                       False,
+                       gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT),
+        'name': (gobject.TYPE_STRING, 'program name', 'program name',
+                 None, gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT),
+        'screen': (gobject.TYPE_OBJECT, 'screen of app', 'screen of app',
+                   gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT),
+        'startup-id': (gobject.TYPE_STRING, 'startup notification id',
+                       'startup notification id',
+                       None, gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT),
+        }
+
+    __gsignals__ = {
+        'message-received': (gobject.SIGNAL_RUN_LAST |
+                             gobject.SIGNAL_NO_RECURSE,
+                             gobject.TYPE_INT,        # out: integer
+                             (gobject.TYPE_INT,       # in:  command id
+                              gobject.TYPE_STRING)),  # in:  command data
+        }
+    
+    # Default commands available to UniqueApp instances. More commands
+    # can be added using the add_command method.
+    commands = {'INVALID':   0,
+                'ACTIVATE': -1,
+                'NEW':      -2,
+                'OPEN':     -3,
+                'CLOSE':    -4}
+    
+    def __init__(self, name, startup_id=None):
+        gobject.GObject.__init__(self)
+        
+        self._is_running = False
+        self._name = name
+        self._screen = gdk.screen_get_default()
+
+        # TODO: Find out what the startup_id is meant to be.
+        self._startup_id = startup_id
+        self.sess_bus = dbus.SessionBus()
+
+        try:
+            # Try to get the object of the already running UniqueApp
+            # instance. If this succeeds, there is another instance
+            # running, therefore we can set the is-running property.
+            self.running_process = self.sess_bus.get_object(self.props.name, "/Factory")
+            self.set_property('is-running', True)
+        except dbus.DBusException:
+            # We got a DBus exception. This means that most likely,
+            # noone is listening at the object path. The is-running is
+            # left at its default False state.
+            self.busname = dbus.service.BusName(self._name, self.sess_bus)
+            self.busobj = UniqueDBusObject(self.sess_bus, "/Factory", self)
+
+    def is_running(self):
+        """ Is another UniqueApp instance running? """
+        return self.get_property('is-running')
+    
+    def send_message(self, command, message):
+        """ Send a message to the running UniqueApp instance. """
+
+        # It makes only sense to send messages if there's another
+        # process waiting to receive them.
+        if not self.get_property('is-running'):
+            raise UniqueNotRunning, "Can't send message to nonexistant other instance"
+        
+        # Validate the command id 
+        if not command in self.commands.values():
+            raise UniqueBadCommand, "Undefined command"
+        
+        return self.running_process.SendMessage(command, message,
+                                                dbus_interface = "org.gtk.PyUniqueApp")
+        
+    def add_command(self, command_name, command_id):
+        """ Adds command_name as a custom command. You must call
+        UniqueApp.add_command() before UniqueApp.send_message() in
+        order to use the newly added command."""
+
+        if command_name in self.commands.keys() or \
+           command_id in self.commands.values():
+            raise UniqueException, "Command ID or name already added"
+
+        self.commands[command_name] = command_id
+        
+    def add_window(self, window):
+        """ Add a window to be watched.. for something. """
+        #
+        # TODO: Need to figure out this one
+        #
+        pass
+
+    def watch_window(self, window):
+        """ Monitor window for startup notifications... whatever. """
+        #
+        # TODO: Need to figure out this one
+        #
+        pass
+
+    def _emit_message_received(self, command, data):
+        """ Emit the message-received signal. Called by the DBus
+        listener object. """
+        self.emit('message-received', command, data)
+        pass
+
+
+    #
+    # Boilerplate GObject code for mapping properties to instance
+    # variables.
+    #
+        
+    def do_get_property(self, prop):
+        """ Actual method for getting a property value from an instance variable """
+        if prop.name == 'is-running':
+            return self._is_running
+        elif prop.name == 'name':
+            return self._name
+        elif prop.name == 'screen':
+            return self._screen
+        elif prop.name == 'startup-id':
+            return self._startup_id
+        else:
+            raise AttributeError, 'unknown property %s' % prop.name
+
+    def do_set_property(self, prop, value):
+        """ Actual method for setting a property value to an instance variable """
+        if prop.name == 'is-running':
+            self._is_running = value
+        elif prop.name == 'name':
+            self._name = value
+        elif prop.name == 'screen':
+            self._screen = value
+        elif prop.name == 'startup-id':
+            self._startup_id = value
+        else:
+            raise AttributeError, 'unknown property %s' % prop.name



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]