From bb6d7ea31a56ebd43eb5e820877edad56f91b9b2 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Tue, 8 Sep 2009 00:41:56 +0800 Subject: Custom event raising, added keycmd plugin & made handler class. --- examples/data/uzbl/scripts/event_manager.py | 228 +++++++++++++++------- examples/data/uzbl/scripts/plugins/dump_config.py | 13 +- examples/data/uzbl/scripts/plugins/echo_keys.py | 7 +- examples/data/uzbl/scripts/plugins/keycmd.py | 97 +++++++++ 4 files changed, 261 insertions(+), 84 deletions(-) create mode 100644 examples/data/uzbl/scripts/plugins/keycmd.py diff --git a/examples/data/uzbl/scripts/event_manager.py b/examples/data/uzbl/scripts/event_manager.py index 5145653..967fe39 100755 --- a/examples/data/uzbl/scripts/event_manager.py +++ b/examples/data/uzbl/scripts/event_manager.py @@ -101,6 +101,18 @@ def counter(): yield i +def iscallable(obj): + '''Return true if the object is callable.''' + + return hasattr(obj, "__call__") + + +def isiterable(obj): + '''Return true if you can iterate over the item.''' + + return hasattr(obj, "__iter__") + + class PluginManager(dict): def __init__(self): @@ -194,19 +206,83 @@ class PluginManager(dict): self.load_plugins() -def export_wrapper(uzbl, function): - '''Return an object that appends the uzbl meta-instance to the front of - the argument queue. I.e. (*args, **kargs) -> (uzbl, *args, **kargs)''' +class CallPrepender(object): + '''Execution argument modifier. Takes (arg, function) then modifies the + function call: + + -> function(*args, **kargs) -> function(arg, *args, **kargs) ->''' + + def __init__(self, uzbl, function): + self.function = function + self.uzbl = uzbl + + def call(self, *args, **kargs): + return self.function(self.uzbl, *args, **kargs) + + +class Handler(object): + + nexthid = counter().next + + def __init__(self, uzbl, event, handler, *args, **kargs): + self._callable = iscallable(handler) + if self._callable: + self._function = handler + self._args = args + self._kargs = kargs + + elif kargs: + raise ArgumentError("cannot supply kargs with a uzbl command") + + elif isiterable(handler): + self._commands = handler + + else: + self._commands = [handler,] + list(args) + + self._uzbl = uzbl + self.event = event + self.hid = self.nexthid() + + + def exec_handler(self, *args, **kargs): + '''Execute either the handler function or send the uzbl commands to + the socket.''' + + if self._callable: + args = args + self._args + kargs = dict(self._kargs.items()+kargs.items()) + self._function(self._uzbl, *args, **kargs) + + else: + for command in self._commands: + if '%s' in command and len(args) == 1: + command.replace('%s', args[0]) + + elif '%s' in command: + for arg in args: + command.replace('%s', arg, 1) + + self._uzbl.send(command) - class Export(object): - def __init__(self, uzbl, function): - self.function = function - self.uzbl = uzbl - def call(self, *args, **kargs): - return self.function(self.uzbl, *args, **kargs) + def __repr__(self): + args = ["event=%s" % self.event, "hid=%d" % self.hid] - return Export(uzbl, function).call + if self._callable: + args.append("function=%r" % self._function) + if self._args: + args.append("args=%r" % self.args) + + if self._kargs: + args.append("kargs=%r" % self.kargs) + + else: + cmdlen = len(self._commands) + cmds = self._commands[0] if cmdlen == 1 else self._commands + args.append("command%s=%r" % ("s" if cmdlen-1 else "", cmds)) + + return "" % ', '.join(args) class UzblInstance(object): @@ -215,44 +291,38 @@ class UzblInstance(object): # Singleton plugin manager. plugins = None - def __init__(self): - '''Initialise event manager.''' + # Internal uzbl config dict. + class ConfigDict(dict): + def __init__(self, setcmd): + self._setcmd = setcmd - # Hold functions exported by plugins. - self._exports = {} + def __setitem__(self, key, value): + '''Updates the config dict and relays any changes back to the + uzbl instance via the set function.''' - class ConfigDict(dict): - def __init__(self, setcmd): - self._setcmd = setcmd + if type(value) == types.BooleanType: + value = int(value) - def __setitem__(self, key, value): - '''Updates the config dict and relays any changes back to the - uzbl instance via the set function.''' + if key in self.keys() and type(value) != type(self[key]): + raise TypeError("%r for %r" % (type(value), key)) - if type(value) == types.BooleanType: - value = int(value) + else: + # All custom variables are strings. + value = "" if value is None else str(value) - if key in self.keys() and type(value) != type(self[key]): - raise TypeError("%r for %r" % (type(value), key)) + self._setcmd(key, value) + dict.__setitem__(self, key, value) - else: - # All custom variables are strings. - value = "" if value is None else str(value) - self._setcmd(key, value) - dict.__setitem__(self, key, value) + def __init__(self): + '''Initialise event manager.''' - self._config = ConfigDict(self.set) + # Hold functions exported by plugins. + self._exports = {} + self._config = self.ConfigDict(self.set) self._running = None - self._cmdbuffer = [] - self.keysheld = [] - self.metaheld = [] - self.mode = "command" - - self.binds = {} - self.handlers = {} - self.nexthid = counter().next + self._handlers = {} # Variables needed for fifo & socket communication with uzbl. self.uzbl_fifo = None @@ -300,8 +370,10 @@ class UzblInstance(object): continue obj = getattr(plugin, attr) - if type(obj) in [types.LambdaType, types.FunctionType]: - obj = export_wrapper(self, obj) + if iscallable(obj): + # Wrap the function in the CallPrepender object to make + # the exposed functions act like instance methods. + obj = CallPrepender(self, obj).call self._exports[attr[7:]] = obj @@ -356,32 +428,44 @@ class UzblInstance(object): def connect(self, event, handler, *args, **kargs): - '''Connect event with handler and return unique handler id. It goes - without saying that if you connect handlers with non-existent events - nothing will happen so be careful. + '''Connect event with handler and return the newly created handler. + Handlers can either be a function or a uzbl command string.''' + + if event not in self._handlers.keys(): + self._handlers[event] = [] + + handler = Handler(self, event, handler, *args, **kargs) + self._handlers[event].append(handler) - If you choose the handler may be a uzbl command and upon receiving the - event the chosen command will be executed by the uzbl instance.''' + print "New event handler:", handler + return handler - if event not in self.handlers.keys(): - self.handlers[event] = {} - id = self.nexthid() - d = {'handler': handler, 'args': args, 'kargs': kargs} + def remove_by_id(self, hid): + '''Remove connected event handler by unique handler id.''' - self.handlers[event][id] = d - echo("added handler for %s: %r" % (event, d)) + for (event, handlers) in self._handlers.items(): + for handler in list(handlers): + if hid != handler.hid: + continue - return id + echo("removed %r" % handler) + handlers.remove(handler) + return + echo('unable to find & remove handler with id: %d' % handler.hid) - def remove(self, id): - '''Remove connected event handler by unique handler id.''' - for event in self.handlers.keys(): - if id in self.handlers[event].keys(): - echo("removed handler %d" % id) - del self.handlers[event][id] + def remove(self, handler): + '''Remove connected event handler.''' + + for (event, handlers) in self._handlers.items(): + if handler in handlers: + echo("removed %r" % handler) + handlers.remove(handler) + return + + echo('unable to find & remove handler: %r' % handler) def set(self, key, value): @@ -432,6 +516,7 @@ class UzblInstance(object): if not msg or msg[0] != "EVENT": # Not an event message + print raw.rstrip() return event, args = msg[1], msg[3] @@ -464,6 +549,7 @@ class UzblInstance(object): if hasattr(plugin, "cleanup"): plugin.cleanup(uzbl) + # Now handle the event "publically". self.dispatch_event(event, args) @@ -471,27 +557,27 @@ class UzblInstance(object): '''Now send the event to any event handlers added with the connect function. In other words: handle plugin's event hooks.''' - if event in self.handlers.keys(): - for hid in self.handlers[event]: + if event in self._handlers: + for handler in self._handlers[event]: try: - handler = self.handlers[event][hid] - print "Executing handler:", event, handler - self.exc_handler(handler, args) + handler.exec_handler(args) except: print_exc() - def exc_handler(self, d, args): - '''Handle handler.''' + def event(self, event, *args, **kargs): + '''Raise a custom event.''' - if type(d['handler']) == types.FunctionType: - handler = d['handler'] - handler(self, args, *d['args'], **d['kargs']) + print "Got custom event:", event, args, kargs - else: - cmd = d['handler'] - self.send(cmd) + if event in self._handlers: + for handler in self._handlers[event]: + try: + handler.exec_handler(*args, **kargs) + + except: + print_ext() if __name__ == "__main__": diff --git a/examples/data/uzbl/scripts/plugins/dump_config.py b/examples/data/uzbl/scripts/plugins/dump_config.py index 381dbf2..ba6543a 100644 --- a/examples/data/uzbl/scripts/plugins/dump_config.py +++ b/examples/data/uzbl/scripts/plugins/dump_config.py @@ -1,11 +1,4 @@ -import pprint - -def dump_config(uzbl, args): - '''Dump the config every time the page finishes loading.''' - - print "%s\n" % pprint.pformat(uzbl.config) - - def init(uzbl): - id = uzbl.connect('LOAD_FINISH', dump_config) - print "Dump config id:", id + commands = ['dump_config', 'dump_config_as_events'] + handler = uzbl.connect('LOAD_FINISH', commands) + print "Added handler with id", handler.hid diff --git a/examples/data/uzbl/scripts/plugins/echo_keys.py b/examples/data/uzbl/scripts/plugins/echo_keys.py index e1a1850..b76a80f 100644 --- a/examples/data/uzbl/scripts/plugins/echo_keys.py +++ b/examples/data/uzbl/scripts/plugins/echo_keys.py @@ -2,7 +2,7 @@ def echo_keys(uzbl, key, print_meta=True): '''Prints key-presses to the terminal.''' keys_pressed = int(uzbl.config['keys_pressed']) + 1 - print "You pressed:", key, "Total keys pressed:", keys_pressed + print "Total keys pressed:", keys_pressed uzbl.config['keys_pressed'] = str(keys_pressed) @@ -10,7 +10,8 @@ def init(uzbl): '''In this function attach all your event hooks using uzbl.connect and uzbl.bind functions.''' - id = uzbl.connect('KEY_PRESS', echo_keys) - print "echo_keys hook id:", id + uzbl.connect('KEY_PRESS', echo_keys) + uzbl.connect('KEY_PRESS', "sh %r" % ("echo %r" % "You just pressed %s")) + # Start a running counter of all keys pressed. uzbl.config['keys_pressed'] = str(0) diff --git a/examples/data/uzbl/scripts/plugins/keycmd.py b/examples/data/uzbl/scripts/plugins/keycmd.py new file mode 100644 index 0000000..d634f32 --- /dev/null +++ b/examples/data/uzbl/scripts/plugins/keycmd.py @@ -0,0 +1,97 @@ +class Keylet(object): + def __init__(self): + self.cmd = "" + self.held = [] + self.modfirst = False + + def __repr__(self): + fmt = "" + if not self.cmd and not self.held: + return fmt % "" + + elif not len(self.held): + return fmt % self.cmd + + helds = '+'.join(["<%s>" % key for key in self.held]) + if not self.cmd: + return fmt % helds + + else: + return fmt % ("%s+%s" % (helds, self.cmd)) + + +class KeycmdTracker(dict): + def get_cmd(self, uzbl): + '''Returns a tuple of the form (keys held, cmdstr)''' + + if uzbl not in self: + return ([], []) + + return self[uzbl] + + + def key_press(self, uzbl, key): + + if key.startswith('Shift_'): + return + + t = self.get_keylet(uzbl) + if key not in t.held: + if not t.held and not t.cmd and len(key) != 1: + t.modfirst = True + + t.held.append(key) + + self.raise_event(uzbl) + + + def key_release(self, uzbl, key): + + t = self.get_keylet(uzbl) + if key in t.held: + t.held.remove(key) + if len(key) == 1: + t.cmd += key + + elif t.modfirst and not len(t.held): + self.clear(uzbl) + + self.raise_event(uzbl) + + + def get_keylet(self, uzbl): + if uzbl not in self: + self.add_instance(uzbl) + + return self[uzbl] + + + def clear(self, uzbl): + t = self.get_keylet(uzbl) + t.cmd = "" + t.modfirst = False + + + def add_instance(self, uzbl): + self[uzbl] = Keylet() + + + def del_instance(self, uzbl): + if uzbl in self: + del uzbl + + + def raise_event(self, uzbl): + '''Raise a custom event.''' + + uzbl.event('KEYCMD_UPDATE', self.get_keylet(uzbl)) + + +keycmd = KeycmdTracker() + +def init(uzbl): + + uzbl.connect('INSTANCE_START', keycmd.add_instance) + uzbl.connect('INSTANCE_STOP', keycmd.del_instance) + uzbl.connect('KEY_PRESS', keycmd.key_press) + uzbl.connect('KEY_RELEASE', keycmd.key_release) -- cgit v1.2.3