From fdd8d07eba9e483baf618b6d8fd928fefb8465f3 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Thu, 15 Oct 2009 00:08:45 +0800 Subject: Added multi-instance managing to event_manager.py 1. Moved plugin directory from './examples/data/scripts/plugins' to './examples/data/'. 2. Broke up the plugin manager class into two small functions. 3. Removed the handler objects ability to have non-callable handlers given that there is a perfectly good on_event.py plugin which can do exactly the same. 4. Gave event_manager daemon abilities similar to the cookie_daemon. 5. Using pid to track the event manager daemons running status. 6. Added the ability to load plugins from multiple locations. 7. Removed all outgoing message queues as this work-around is no longer required after the newly added --connect-socket uzbl-core ability. 8. Removed native stdin/fifo reading ability. Use socat if required. 9. Updated uzbl-browser script to load example cookie_daemon if cookie_daemon is not in $XDG_DATA_HOME/uzbl/scripts/ 10. Added a new event_manager.py launcher uzbl-daemon. 11. Updated make test-dev-browser target to test uzbl-daemon also. 12. Added init like {start|stop|restart} to the event manager. 13. Added a fourth 'list' option to {start|stop|..} to list the plugins and dirs of each plugin that would be loaded by the event manager. --- examples/data/uzbl/plugins/bind.py | 400 +++++++++++++++++++++++++++++++++++++ 1 file changed, 400 insertions(+) create mode 100644 examples/data/uzbl/plugins/bind.py (limited to 'examples/data/uzbl/plugins/bind.py') diff --git a/examples/data/uzbl/plugins/bind.py b/examples/data/uzbl/plugins/bind.py new file mode 100644 index 0000000..d62872f --- /dev/null +++ b/examples/data/uzbl/plugins/bind.py @@ -0,0 +1,400 @@ +'''Plugin provides support for binds in uzbl. + +For example: + event BIND ZZ = exit -> bind('ZZ', 'exit') + event BIND o _ = uri %s -> bind('o _', 'uri %s') + event BIND fl* = sh 'echo %s' -> bind('fl*', "sh 'echo %s'") + +And it is also possible to execute a function on activation: + bind('DD', myhandler) +''' + +import sys +import re +from event_manager import config, counter, iscallable, isiterable + +# Export these variables/functions to uzbl. +__export__ = ['bind', 'del_bind', 'del_bind_by_glob', 'get_binds'] + +# Hold the bind lists per uzbl instance. +UZBLS = {} + +# Commonly used regular expressions. +starts_with_mod = re.compile('^<([A-Z][A-Za-z0-9-_]+)>') +find_prompts = re.compile('<([^:>]*):>').split + +# For accessing a bind glob stack. +MOD_CMD, ON_EXEC, HAS_ARGS, GLOB = range(4) + + +class BindParseError(Exception): + pass + + +def echo(msg): + if config['verbose']: + print 'bind plugin:', msg + + +def error(msg): + sys.stderr.write('bind plugin: error: %s\n' % msg) + + +def ismodbind(glob): + '''Return True if the glob specifies a modbind.''' + + return bool(starts_with_mod.match(glob)) + + +def sort_mods(glob): + '''Mods are sorted in the keylet.to_string() result so make sure that + bind commands also have their mod keys sorted.''' + + mods = [] + while True: + match = starts_with_mod.match(glob) + if not match: + break + + end = match.span()[1] + mods.append(glob[:end]) + glob = glob[end:] + + return '%s%s' % (''.join(sorted(mods)), glob) + + +def add_instance(uzbl, *args): + UZBLS[uzbl] = {'binds': [], 'depth': 0, 'filter': [], + 'args': [], 'last_mode': ''} + + +def del_instance(uzbl, *args): + if uzbl in UZBLS: + del UZBLS[uzbl] + + +def get_bind_dict(uzbl): + '''Return the bind dict for the uzbl instance.''' + + if uzbl not in UZBLS: + add_instance(uzbl) + + return UZBLS[uzbl] + + +def get_binds(uzbl): + '''Return the bind list for the uzbl instance.''' + + return get_bind_dict(uzbl)['binds'] + + +def get_stack_depth(uzbl): + '''Return the stack for the uzbl instance.''' + + return get_bind_dict(uzbl)['depth'] + + +def get_filtered_binds(uzbl): + '''Return the bind list for the uzbl instance or return the filtered + bind list thats on the current stack.''' + + bind_dict = get_bind_dict(uzbl) + if bind_dict['depth']: + return list(bind_dict['filter']) + + return list(bind_dict['binds']) + + +def del_bind(uzbl, bind): + '''Delete bind object if bind in the uzbl binds.''' + + binds = get_binds(uzbl) + if bind in binds: + binds.remove(bind) + uzbl.event('DELETED_BIND', bind) + return True + + return False + + +def del_bind_by_glob(uzbl, glob): + '''Delete bind by glob if bind in the uzbl binds.''' + + binds = get_binds(uzbl) + for bind in list(binds): + if bind.glob == glob: + binds.remove(bind) + uzbl.event('DELETED_BIND', bind) + return True + + return False + + +class Bind(object): + + nextbid = counter().next + + def __init__(self, glob, handler, *args, **kargs): + self.callable = iscallable(handler) + + if not glob: + raise ArgumentError('glob cannot be blank') + + if self.callable: + self.function = handler + self.args = args + self.kargs = kargs + + elif kargs: + raise ArgumentError('cannot supply kargs for uzbl commands') + + elif isiterable(handler): + self.commands = handler + + else: + self.commands = [handler,] + list(args) + + self.glob = glob + self.bid = self.nextbid() + + self.split = split = find_prompts(glob) + self.prompts = split[1::2] + + # Check that there is nothing like: fl** + for glob in split[:-1:2]: + if glob.endswith('*'): + msg = "token '*' not at the end of a prompt bind: %r" % split + raise BindParseError(msg) + + # Check that there is nothing like: fl_ + for glob in split[2::2]: + if not glob: + msg = 'found null segment after first prompt: %r' % split + raise BindParseError(msg) + + self.stack = [] + + for glob in split[::2]: + # Is the binding a MODCMD or KEYCMD: + mod_cmd = ismodbind(glob) + + # Execute the command on UPDATES or EXEC's: + on_exec = True if glob.endswith('_') else False + + # Does the command store arguments: + has_args = True if glob[-1] in ['*', '_'] else False + glob = glob[:-1] if has_args else glob + + self.stack.append((mod_cmd, on_exec, has_args, glob)) + + + def __repr__(self): + args = ['glob=%r' % self.glob, 'bid=%d' % self.bid] + + 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) + + +def exec_bind(uzbl, bind, *args, **kargs): + '''Execute bind objects.''' + + if bind.callable: + args += bind.args + kargs = dict(bind.kargs.items()+kargs.items()) + bind.function(uzbl, *args, **kargs) + return + + if kargs: + raise ArgumentError('cannot supply kargs for uzbl commands') + + commands = [] + + for cmd in bind.commands: + if '%s' in cmd: + if len(args) > 1: + for arg in args: + cmd = cmd.replace('%s', arg, 1) + + elif len(args) == 1: + cmd = cmd.replace('%s', args[0]) + + uzbl.send(cmd) + + +def bind(uzbl, glob, handler, *args, **kargs): + '''Add a bind handler object.''' + + # Mods come from the keycmd sorted so make sure the modkeys in the bind + # command are sorted too. + glob = sort_mods(glob) + + del_bind_by_glob(uzbl, glob) + binds = get_binds(uzbl) + + bind = Bind(glob, handler, *args, **kargs) + binds.append(bind) + + print bind + uzbl.event('ADDED_BIND', bind) + + +def parse_bind_event(uzbl, args): + '''Break "event BIND fl* = js follownums.js" into (glob, command).''' + + if not args: + return error('missing bind arguments') + + split = map(unicode.strip, args.split('=', 1)) + if len(split) != 2: + return error('missing "=" in bind definition: %r' % args) + + glob, command = split + bind(uzbl, glob, command) + + +def set_stack_mode(uzbl, prompt): + if uzbl.get_mode() != 'stack': + uzbl.set_mode('stack') + + if prompt: + prompt = "%s: " % prompt + + uzbl.set('keycmd_prompt', prompt) + + +def clear_stack(uzbl, mode): + bind_dict = get_bind_dict(uzbl) + if mode != "stack" and bind_dict['last_mode'] == "stack": + uzbl.set('keycmd_prompt', '') + + if mode != "stack": + bind_dict = get_bind_dict(uzbl) + bind_dict['filter'] = [] + bind_dict['depth'] = 0 + bind_dict['args'] = [] + + bind_dict['last_mode'] = mode + + +def filter_bind(uzbl, bind_dict, bind): + '''Remove a bind from the stack filter list.''' + + if bind in bind_dict['filter']: + bind_dict['filter'].remove(bind) + + if not bind_dict['filter']: + uzbl.set_mode() + + +def match_and_exec(uzbl, bind, depth, keycmd): + bind_dict = get_bind_dict(uzbl) + mode_cmd, on_exec, has_args, glob = bind.stack[depth] + + if has_args: + if not keycmd.startswith(glob): + filter_bind(uzbl, bind_dict, bind) + return False + + args = [keycmd[len(glob):],] + + elif keycmd != glob: + filter_bind(uzbl, bind_dict, bind) + return False + + else: + args = [] + + execindex = len(bind.stack)-1 + if execindex == depth == 0: + exec_bind(uzbl, bind, *args) + if not has_args: + uzbl.clear_keycmd() + + return True + + elif depth != execindex: + if bind_dict['depth'] == depth: + bind_dict['filter'] = [bind,] + bind_dict['args'] += args + bind_dict['depth'] = depth + 1 + + else: + if bind not in bind_dict['filter']: + bind_dict['filter'].append(bind) + + set_stack_mode(uzbl, bind.prompts[depth]) + return False + + args = bind_dict['args'] + args + exec_bind(uzbl, bind, *args) + if on_exec: + uzbl.set_mode() + + return True + + +def keycmd_update(uzbl, keylet): + depth = get_stack_depth(uzbl) + keycmd = keylet.to_string() + for bind in get_filtered_binds(uzbl): + t = bind.stack[depth] + if t[MOD_CMD] or t[ON_EXEC]: + continue + + match_and_exec(uzbl, bind, depth, keycmd) + + +def keycmd_exec(uzbl, keylet): + depth = get_stack_depth(uzbl) + keycmd = keylet.to_string() + for bind in get_filtered_binds(uzbl): + t = bind.stack[depth] + if t[MOD_CMD] or not t[ON_EXEC]: + continue + + match_and_exec(uzbl, bind, depth, keycmd) + + +def modcmd_update(uzbl, keylet): + depth = get_stack_depth(uzbl) + keycmd = keylet.to_string() + for bind in get_filtered_binds(uzbl): + t = bind.stack[depth] + if not t[MOD_CMD] or t[ON_EXEC]: + continue + + match_and_exec(uzbl, bind, depth, keycmd) + + +def modcmd_exec(uzbl, keylet): + depth = get_stack_depth(uzbl) + keycmd = keylet.to_string() + for bind in get_filtered_binds(uzbl): + t = bind.stack[depth] + if not t[MOD_CMD] or not t[ON_EXEC]: + continue + + match_and_exec(uzbl, bind, depth, keycmd) + + +def init(uzbl): + connects = {'BIND': parse_bind_event, + 'KEYCMD_UPDATE': keycmd_update, + 'MODCMD_UPDATE': modcmd_update, + 'KEYCMD_EXEC': keycmd_exec, + 'MODCMD_EXEC': modcmd_exec, + 'MODE_CHANGED': clear_stack} + + uzbl.connect_dict(connects) -- cgit v1.2.3