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/scripts/event_manager.py | 865 ++++++++++++--------- examples/data/uzbl/scripts/plugins/bind.py | 374 --------- examples/data/uzbl/scripts/plugins/config.py | 87 --- examples/data/uzbl/scripts/plugins/keycmd.py | 382 --------- examples/data/uzbl/scripts/plugins/mode.py | 159 ---- examples/data/uzbl/scripts/plugins/on_event.py | 117 --- .../data/uzbl/scripts/plugins/plugin_template.py | 75 -- examples/data/uzbl/scripts/plugins/progress_bar.py | 158 ---- 8 files changed, 485 insertions(+), 1732 deletions(-) delete mode 100644 examples/data/uzbl/scripts/plugins/bind.py delete mode 100644 examples/data/uzbl/scripts/plugins/config.py delete mode 100644 examples/data/uzbl/scripts/plugins/keycmd.py delete mode 100644 examples/data/uzbl/scripts/plugins/mode.py delete mode 100644 examples/data/uzbl/scripts/plugins/on_event.py delete mode 100644 examples/data/uzbl/scripts/plugins/plugin_template.py delete mode 100644 examples/data/uzbl/scripts/plugins/progress_bar.py (limited to 'examples/data/uzbl/scripts') diff --git a/examples/data/uzbl/scripts/event_manager.py b/examples/data/uzbl/scripts/event_manager.py index 2e84ded..271c65e 100755 --- a/examples/data/uzbl/scripts/event_manager.py +++ b/examples/data/uzbl/scripts/event_manager.py @@ -24,30 +24,19 @@ E V E N T _ M A N A G E R . P Y Event manager for uzbl written in python. -Usage -===== - - uzbl | $XDG_DATA_HOME/uzbl/scripts/event_manager.py - -Todo -==== - - - Command line options including supplying a list of plugins to load or not - load (default is load all plugins in the plugin_dir). - - Spell checking. - - ''' import imp import os import sys -import select import re import types import socket import pprint import time +import atexit +from select import select +from signal import signal, SIGTERM from optparse import OptionParser from traceback import print_exc @@ -69,13 +58,21 @@ def xdghome(key, default): # Setup xdg paths. DATA_DIR = os.path.join(xdghome('DATA', '.local/share/'), 'uzbl/') +CACHE_DIR = os.path.join(xdghome('CACHE', '.cache/'), 'uzbl/') # Config dict (NOT the same as the uzbl.config). config = { - 'verbose': False, - 'plugin_dir': "$XDG_DATA_HOME/uzbl/scripts/plugins/", - 'plugins_load': [], - 'plugins_ignore': [], + 'verbose': False, + 'daemon_mode': True, + + 'plugins_load': [], + 'plugins_ignore': [], + + 'plugin_dirs': [os.path.join(DATA_DIR, 'plugins/'), + '/usr/local/share/uzbl/examples/data/uzbl/plugins/'], + + 'server_socket': os.path.join(CACHE_DIR, 'event_daemon'), + 'pid_file': os.path.join(CACHE_DIR, 'event_daemon.pid'), } @@ -122,223 +119,265 @@ def isiterable(obj): return hasattr(obj, "__iter__") -class PluginManager(dict): - def __init__(self): +def find_plugins(plugin_dirs): + '''Find all event manager plugins in the plugin dirs and return a + dictionary of {'plugin-name.py': '/full/path/to/plugin-name.py', ...}''' - plugin_dir = os.path.expandvars(config['plugin_dir']) - self.plugin_dir = os.path.realpath(plugin_dir) - if not os.path.exists(self.plugin_dir): - os.makedirs(self.plugin_dir) + plugins = {} + + for plugin_dir in plugin_dirs: + plugin_dir = os.path.realpath(os.path.expandvars(plugin_dir)) + if not os.path.isdir(plugin_dir): + continue - self.load_plugins() + for file in os.listdir(plugin_dir): + if not file.lower().endswith('.py'): + continue + path = os.path.join(plugin_dir, file) + if not os.path.isfile(path): + continue - def _find_all_plugins(self): - '''Find all python scripts in plugin dir and return a list of - locations and imp moduleinfo's.''' + if file not in plugins: + plugins[file] = plugin_dir - dirlist = os.listdir(self.plugin_dir) - pythonfiles = filter(lambda s: s.endswith('.py'), dirlist) + return plugins - plugins = [] - for filename in pythonfiles: - plugins.append(filename[:-3]) +def load_plugins(plugin_dirs, load=[], ignore=[]): + '''Load event manager plugins found in the plugin_dirs.''' - return plugins + # Find the plugins in the plugin_dirs. + found = find_plugins(plugin_dirs) + if load: + # Ignore anything not in the load list. + for plugin in found.keys(): + if plugin not in load: + del found[plugin] - def _unload_plugin(self, name, remove_pyc=True): - '''Unload specific plugin and remove all waste in sys.modules + if ignore: + # Ignore anything in the ignore list. + for plugin in found.keys(): + if plugin in ignore: + del found[plugin] - Notice: manual manipulation of sys.modules is very un-pythonic but I - see no other way to make sure you have 100% unloaded the module. Also - this allows us to implement a reload plugins function.''' + # Print plugin list to be loaded. + pprint.pprint(found) - allmodules = sys.modules.keys() - allrefs = filter(lambda s: s.startswith("%s." % name), allmodules) + loaded = {} + # Load all found plugins into the loaded dict. + for (filename, dir) in found.items(): + name = filename[:-3] + info = imp.find_module(name, [dir,]) + plugin = imp.load_module(name, *info) + loaded[(dir, filename)] = plugin - for ref in allrefs: - del sys.modules[ref] + return loaded - if name in sys.modules.keys(): - del sys.modules[name] - if name in self: - del self[name] +def daemonize(): + '''Daemonize the process using the Stevens' double-fork magic.''' - if remove_pyc: - pyc = os.path.join(self.plugin_dir, '%s.pyc' % name) - if os.path.exists(pyc): - os.remove(pyc) + try: + if os.fork(): + os._exit(0) + except OSError: + print_exc() + sys.stderr.write("fork #1 failed") + sys.exit(1) - def load_plugins(self): + os.chdir('/') + os.setsid() + os.umask(0) - if config['plugins_load']: - pluginlist = config['plugins_load'] + try: + if os.fork(): + os._exit(0) - else: - pluginlist = self._find_all_plugins() - for name in config['plugins_ignore']: - if name in pluginlist: - pluginlist.remove(name) + except OSError: + print_exc() + sys.stderr.write("fork #2 failed") + sys.exit(1) - for name in pluginlist: - # Make sure the plugin isn't already loaded. - self._unload_plugin(name) + sys.stdout.flush() + sys.stderr.flush() - try: - moduleinfo = imp.find_module(name, [self.plugin_dir,]) - plugin = imp.load_module(name, *moduleinfo) - self[name] = plugin + devnull = '/dev/null' + stdin = file(devnull, 'r') + stdout = file(devnull, 'a+') + stderr = file(devnull, 'a+', 0) - except: - raise + os.dup2(stdin.fileno(), sys.stdin.fileno()) + os.dup2(stdout.fileno(), sys.stdout.fileno()) + os.dup2(stderr.fileno(), sys.stderr.fileno()) - if self.keys(): - echo("loaded plugin(s): %s" % ', '.join(self.keys())) +def make_dirs(path): + '''Make all basedirs recursively as required.''' - def reload_plugins(self): - '''Unload all loaded plugins then run load_plugins() again. + dirname = os.path.dirname(path) + if not os.path.isdir(dirname): + os.makedirs(dirname) - IMPORTANT: It is crucial that the event handler be deleted if you - are going to unload any modules because there is now way to track - which module created wich handler.''' - for plugin in self.keys(): - self._unload_plugin(plugin) +def make_pid_file(pid_file): + '''Make pid file at given pid_file location.''' - self.load_plugins() + make_dirs(pid_file) + file = open(pid_file, 'w') + file.write('%d' % os.getpid()) + file.close() -class CallPrepender(object): - '''Execution argument modifier. Takes (arg, function) then modifies the - function call: +def del_pid_file(pid_file): + '''Delete pid file at given pid_file location.''' - -> function(*args, **kargs) -> function(arg, *args, **kargs) ->''' + if os.path.isfile(pid_file): + os.remove(pid_file) - def __init__(self, uzbl, function): - self.function = function - self.uzbl = uzbl - def call(self, *args, **kargs): - return self.function(self.uzbl, *args, **kargs) +def get_pid(pid_file): + '''Read pid from pid_file.''' + try: + file = open(pid_file, 'r') + strpid = file.read() + file.close() + pid = int(strpid.strip()) + return pid -class Handler(object): + except: + print_exc() + return None - nexthid = counter().next - def __init__(self, event, handler, *args, **kargs): - self.callable = iscallable(handler) - if self.callable: - self.function = handler - self.args = args - self.kargs = kargs +def pid_running(pid): + '''Returns True if a process with the given pid is running.''' - elif kargs: - raise ArgumentError("cannot supply kargs with a uzbl command") + try: + os.kill(pid, 0) - elif isiterable(handler): - self.commands = handler + except OSError: + return False - else: - self.commands = [handler,] + list(args) + else: + return True + + +def term_process(pid): + '''Send a SIGTERM signal to the process with the given pid.''' + + if not pid_running(pid): + return False + + os.kill(pid, SIGTERM) + + start = time.time() + while True: + if not pid_running(pid): + return True + + if time.time() - start > 5: + raise OSError('failed to stop process with pid: %d' % pid) + + time.sleep(0.25) + + +def prepender(function, *pre_args): + '''Creates a wrapper around a callable object injecting a list of + arguments before the called arguments.''' + locals = (function, pre_args) + def _prepender(*args, **kargs): + (function, pre_args) = locals + return function(*(pre_args + args), **kargs) + + return _prepender + + +class EventHandler(object): + + nexthid = counter().next + + def __init__(self, event, handler, *args, **kargs): + if not iscallable(handler): + raise ArgumentError("EventHandler object requires a callable " + "object function for the handler argument not: %r" % handler) + + self.function = handler + self.args = args + self.kargs = kargs self.event = event self.hid = self.nexthid() def __repr__(self): - args = ["event=%s" % self.event, "hid=%d" % self.hid] + args = ["event=%s" % self.event, "hid=%d" % self.hid, + "function=%r" % self.function] - if self.callable: - args.append("function=%r" % self.function) - if self.args: - args.append("args=%r" % self.args) + 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)) + if self.kargs: + args.append("kargs=%r" % self.kargs) return "" % ', '.join(args) class UzblInstance(object): - '''Event manager for a uzbl instance.''' - - # Singleton plugin manager. - plugins = None + def __init__(self, parent, client_socket): - def __init__(self): - '''Initialise event manager.''' - - # Hold functions exported by plugins. + # Internal variables. self._exports = {} - self._running = None - self._buffer = '' - self._handlers = {} + self._parent = parent + self._client_socket = client_socket - # Variables needed for fifo & socket communication with uzbl. - self.uzbl_fifo = None - self.uzbl_socket = None - self._fifo_cmd_queue = [] - self._socket_cmd_queue = [] - self._socket = None - self.send = self._send_socket + self.buffer = '' - if not self.plugins: - self.plugins = PluginManager() - - # Call the init() function in every plugin which then setup their - # respective hooks (event handlers, binds or timers). + # Call the init() function in every plugin. Inside the init function + # is where the plugins insert the hooks into the event system. self._init_plugins() - def __getattribute__(self, name): + def __getattribute__(self, attr): '''Expose any exported functions before class functions.''' - if not name.startswith('_'): + if not attr.startswith('_'): exports = object.__getattribute__(self, '_exports') - if name in exports: - return exports[name] + if attr in exports: + return exports[attr] - return object.__getattribute__(self, name) + return object.__getattribute__(self, attr) def _init_plugins(self): '''Call the init() function in every plugin and expose all exposable functions in the plugins root namespace.''' + plugins = self._parent['plugins'] + # Map all plugin exports - for (name, plugin) in self.plugins.items(): + for (name, plugin) in plugins.items(): if not hasattr(plugin, '__export__'): continue for export in plugin.__export__: if export in self._exports: - orig = self._exports[export] - raise KeyError("already exported attribute: %r" % export) + raise KeyError("conflicting export: %r" % export) obj = getattr(plugin, export) if iscallable(obj): - # Wrap the function in the CallPrepender object to make - # the exposed functions act like instance methods. - obj = CallPrepender(self, obj).call + obj = prepender(obj, self) self._exports[export] = obj echo("exposed attribute(s): %s" % ', '.join(self._exports.keys())) # Now call the init function in all plugins. - for (name, plugin) in self.plugins.items(): + for (name, plugin) in plugins.items(): try: plugin.init(self) @@ -347,82 +386,15 @@ class UzblInstance(object): raise - def _init_uzbl_socket(self, uzbl_socket=None, timeout=None): - '''Store socket location and open socket connection to uzbl socket.''' - - if uzbl_socket is None: - uzbl_socket = self.uzbl_socket - - if not uzbl_socket: - error("no socket location.") - return - - if not os.path.exists(uzbl_socket): - if timeout is None: - error("uzbl socket doesn't exist: %r" % uzbl_socket) - return - - waitlimit = time.time() + timeout - echo("waiting for uzbl socket: %r" % uzbl_socket) - while not os.path.exists(uzbl_socket): - time.sleep(0.25) - if time.time() > waitlimit: - error("timed out waiting for socket: %r" % uzbl_socket) - return - - self.uzbl_socket = uzbl_socket - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(self.uzbl_socket) - self._socket = sock - - - def _close_socket(self): - '''Close the socket used for communication with the uzbl instance. - This function is normally called upon receiving the INSTANCE_EXIT - event.''' - - if self._socket: - self._socket.close() - - self.uzbl_socket = self._socket = None - - - def _flush(self): - '''Flush messages from the outgoing queue to the uzbl instance.''' - - if len(self._fifo_cmd_queue) and self.uzbl_fifo: - if os.path.exists(self.uzbl_fifo): - h = open(self.uzbl_fifo, 'w') - while len(self._fifo_cmd_queue): - msg = self._fifo_cmd_queue.pop(0) - print '<-- %s' % msg - h.write(("%s\n" % msg).encode('utf-8')) - - h.close() - - if len(self._socket_cmd_queue) and self.uzbl_socket: - if not self._socket and os.path.exists(self.uzbl_socket): - self._init_uzbl_socket() - - if self._socket: - while len(self._socket_cmd_queue): - msg = self._socket_cmd_queue.pop(0) - print '<-- %s' % msg - self._socket.send(("%s\n" % msg).encode('utf-8')) - - - def _send_fifo(self, msg): - '''Send a command to the uzbl instance via the fifo socket.''' - - self._fifo_cmd_queue.append(msg) - self._flush() - - - def _send_socket(self, msg): + def send(self, msg): '''Send a command to the uzbl instance via the socket file.''' - self._socket_cmd_queue.append(msg) - self._flush() + if self._client_socket: + print '<-- %s' % msg + self._client_socket.send(("%s\n" % msg).encode('utf-8')) + + else: + print '!-- %s' % msg def connect(self, event, handler, *args, **kargs): @@ -432,11 +404,9 @@ class UzblInstance(object): if event not in self._handlers.keys(): self._handlers[event] = [] - handler = Handler(event, handler, *args, **kargs) - self._handlers[event].append(handler) - - print handler - return handler + handlerobj = EventHandler(event, handler, *args, **kargs) + self._handlers[event].append(handlerobj) + print handlerobj def connect_dict(self, connect_dict): @@ -477,216 +447,356 @@ class UzblInstance(object): echo('unable to find & remove handler: %r' % handler) - def listen_from_fd(self, fd): - '''Polls for event messages from fd.''' + def exec_handler(self, handler, *args, **kargs): + '''Execute event handler function.''' + + args += handler.args + kargs = dict(handler.kargs.items()+kargs.items()) + handler.function(self, *args, **kargs) + + + def event(self, event, *args, **kargs): + '''Raise a custom event.''' + + # Silence _printing_ of geo events while debugging. + if event != "GEOMETRY_CHANGED": + print "--> %s %s %s" % (event, args, '' if not kargs else kargs) + + if event not in self._handlers: + return + + for handler in self._handlers[event]: + try: + self.exec_handler(handler, *args, **kargs) + + except: + print_exc() + + + def close(self): + '''Close the client socket and clean up.''' try: - self._running = True - while self._running: - if select.select([fd,], [], [], 1)[0]: - self.read_from_fd(fd) - continue + self._client_socket.close() - self._flush() + except: + pass - except KeyboardInterrupt: - print + for (name, plugin) in self._parent['plugins'].items(): + if hasattr(plugin, 'cleanup'): + plugin.cleanup(self) + + del self._exports + del self._handlers + del self._client_socket + + +class UzblEventDaemon(dict): + def __init__(self): + + # Init variables and dict keys. + dict.__init__(self, {'uzbls': {}}) + self.running = None + self.server_socket = None + self.socket_location = None + + # Register that the event daemon server has started by creating the + # pid file. + make_pid_file(config['pid_file']) + + # Register a function to clean up the socket and pid file on exit. + atexit.register(self.quit) + + # Make SIGTERM act orderly. + signal(SIGTERM, lambda signum, stack_frame: sys.exit(1)) + + # Load plugins, first-build of the plugins may be a costly operation. + self['plugins'] = load_plugins(config['plugin_dirs'], + config['plugins_load'], config['plugins_ignore']) + + + def _create_server_socket(self): + '''Create the event manager daemon socket for uzbl instance duplex + communication.''' + + server_socket = config['server_socket'] + server_socket = os.path.realpath(os.path.expandvars(server_socket)) + self.socket_location = server_socket + + # Delete socket if it exists. + if os.path.exists(server_socket): + os.remove(server_socket) + + self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.server_socket.bind(server_socket) + self.server_socket.listen(5) + + + def _close_server_socket(self): + '''Close and delete the server socket.''' + + try: + self.server_socket.close() + self.server_socket = None + + if os.path.exists(self.socket_location): + os.remove(self.socket_location) except: - #print_exc() - raise + pass - def read_from_fd(self, fd): - '''Reads event messages from a single fd.''' + def run(self): + '''Main event daemon loop.''' - raw = fd.readline() - if not raw: - # Read null byte (i.e. uzbl closed). - self._running = False - return + if config['daemon_mode']: + echo('entering daemon mode.') + daemonize() + # The pid has changed so update the pid file. + make_pid_file(config['pid_file']) - msg = raw.strip().split(' ', 3) + # Create event daemon socket. + self._create_server_socket() + echo('listening on: %s' % self.socket_location) - if not msg or msg[0] != "EVENT": - # Not an event message - print "---", raw.rstrip() - return + # Now listen for incoming connections and or data. + self.listen() - event, args = msg[1], msg[3] - self.handle_event(event, args) + # Clean up. + self.quit() - def listen_from_uzbl_socket(self, uzbl_socket): - '''Polls for event messages from a single uzbl socket.''' + def listen(self): + '''Accept incoming connections and constantly poll instance sockets + for incoming data.''' - self._init_uzbl_socket(uzbl_socket, 10) + self.running = True + while self.running: - if not self._socket: - error("failed to init socket: %r" % uzbl_socket) - return + sockets = [self.server_socket,] + self['uzbls'].keys() + + read, _, error = select(sockets, [], sockets, 1) + + if self.server_socket in read: + self.accept_connection() + read.remove(self.server_socket) + + for client in read: + self.read_socket(client) + + for client in error: + error('Unknown error on socket: %r' % client) + self.close_connection(client) + + + def read_socket(self, client): + '''Read data from an instance socket and pass to the uzbl objects + event handler function.''' - self._flush() try: - self._running = True - while self._running: - if select.select([self._socket], [], [], 1): - self.read_from_uzbl_socket() - continue + uzbl = self['uzbls'][client] + raw = unicode(client.recv(8192), 'utf-8', 'ignore') + if not raw: + # Read null byte, close socket. + return self.close_connection(client) - self._flush() + uzbl.buffer += raw + msgs = uzbl.buffer.split('\n') + uzbl.buffer = msgs.pop() - except KeyboardInterrupt: - print + for msg in msgs: + self.parse_msg(uzbl, msg) except: - #print_exc() raise - def read_from_uzbl_socket(self): - '''Reads event messages from a uzbl socket.''' + def parse_msg(self, uzbl, msg): + '''Parse an incoming msg from a uzbl instance. All non-event messages + will be printed here and not be passed to the uzbl instance event + handler function.''' - raw = unicode(self._socket.recv(8192), 'utf-8', 'ignore') - if not raw: - # Read null byte - self._running = False + msg = msg.strip() + if not msg: return - self._buffer += raw - msgs = self._buffer.split("\n") - self._buffer = msgs.pop() + cmd = _RE_FINDSPACES.split(msg, 3) + if not cmd or cmd[0] != 'EVENT': + # Not an event message. + print '---', msg + return - for msg in msgs: - msg = msg.rstrip() - if not msg: - continue + if len(cmd) < 4: + cmd.append('') - cmd = _RE_FINDSPACES.split(msg, 3) - if not cmd or cmd[0] != "EVENT": - # Not an event message - print msg.rstrip() - continue + event, args = cmd[2], cmd[3] - if len(cmd) < 4: - cmd.append('') + try: + uzbl.event(event, args) - event, args = cmd[2], cmd[3] - try: - self.handle_event(event, args) + except: + print_exc() - except: - #print_exc() - raise + def accept_connection(self): + '''Accept incoming connection to the server socket.''' - def handle_event(self, event, args): - '''Handle uzbl events internally before dispatch.''' + client_socket = self.server_socket.accept()[0] - if event == 'FIFO_SET': - self.uzbl_fifo = args - self._flush() + uzbl = UzblInstance(self, client_socket) + self['uzbls'][client_socket] = uzbl - elif event == 'SOCKET_SET': - if not self.uzbl_socket or not self._socket: - self._init_uzbl_socket(args) - self._flush() - elif event == 'INSTANCE_EXIT': - self._close_socket() - self._running = False - for (name, plugin) in self.plugins.items(): - if hasattr(plugin, "cleanup"): - plugin.cleanup(uzbl) + def close_connection(self, client): + '''Clean up after instance close.''' - # Now handle the event "publically". - self.event(event, args) + try: + if client not in self['uzbls']: + return + uzbl = self['uzbls'][client] + uzbl.close() + del self['uzbls'][client] - def exec_handler(self, handler, *args, **kargs): - '''Execute either the handler function or send the handlers uzbl - commands via the socket.''' + except: + print_exc() - if handler.callable: - args = args + handler.args - kargs = dict(handler.kargs.items()+kargs.items()) - handler.function(uzbl, *args, **kargs) - else: - if kargs: - raise ArgumentError('cannot supply kargs for uzbl commands') + def quit(self): + '''Close all instance socket objects, server socket and delete the + pid file.''' - for command in handler.commands: - if '%s' in command: - if len(args) > 1: - for arg in args: - command = command.replace('%s', arg, 1) + echo('shutting down event manager.') - elif len(args) == 1: - command = command.replace('%s', args[0]) + for client in self['uzbls'].keys(): + self.close_connection(client) - uzbl.send(command) + echo('unlinking: %r' % self.socket_location) + self._close_server_socket() + echo('deleting pid file: %r' % config['pid_file']) + del_pid_file(config['pid_file']) - def event(self, event, *args, **kargs): - '''Raise a custom event.''' - # Silence _printing_ of geo events while still debugging. - if event != "GEOMETRY_CHANGED": - print "--> %s %s %s" % (event, args, '' if not kargs else kargs) +def stop(): + '''Stop the event manager daemon.''' - if event in self._handlers: - for handler in self._handlers[event]: - try: - self.exec_handler(handler, *args, **kargs) + pid_file = config['pid_file'] + if not os.path.isfile(pid_file): + return echo('no running daemon found.') - except: - #print_exc() - raise + echo('found pid file: %r' % pid_file) + pid = get_pid(pid_file) + if not pid_running(pid): + echo('no process with pid: %d' % pid) + return os.remove(pid_file) + echo("terminating process with pid: %d" % pid) + term_process(pid) + if os.path.isfile(pid_file): + os.remove(pid_file) -if __name__ == "__main__": - #uzbl = UzblInstance().listen_from_fd(sys.stdin) + echo('stopped event daemon.') + + +def start(): + '''Start the event manager daemon.''' + + pid_file = config['pid_file'] + if os.path.isfile(pid_file): + echo('found pid file: %r' % pid_file) + pid = get_pid(pid_file) + if pid_running(pid): + return echo('event daemon already started with pid: %d' % pid) + + echo('no process with pid: %d' % pid) + os.remove(pid_file) + + echo('starting event manager.') + UzblEventDaemon().run() + + +def restart(): + '''Restart the event manager daemon.''' + + echo('restarting event manager daemon.') + stop() + start() + + +def list_plugins(): + '''List all the plugins being loaded by the event daemon.''' - parser = OptionParser() - parser.add_option('-s', '--uzbl-socket', dest='socket', - action="store", metavar="SOCKET", - help="read event messages from uzbl socket.") + plugins = find_plugins(config['plugin_dirs']) + dirs = {} + for (plugin, dir) in plugins.items(): + if dir not in dirs: + dirs[dir] = [] + + dirs[dir].append(plugin) + + for (index, (dir, plugin_list)) in enumerate(sorted(dirs.items())): + if index: + print + + print "%s:" % dir + for plugin in sorted(plugin_list): + print " %s" % plugin + + +if __name__ == "__main__": + usage = "usage: %prog [options] {start|stop|restart|list}" + parser = OptionParser(usage=usage) parser.add_option('-v', '--verbose', dest='verbose', action="store_true", help="print verbose output.") - parser.add_option('-d', '--plugin-dir', dest='plugin_dir', action="store", - metavar="DIR", help="change plugin directory.") + parser.add_option('-d', '--plugin-dirs', dest='plugin_dirs', action="store", + metavar="DIRS", help="Specify plugin directories in the form of "\ + "'dir1:dir2:dir3'.") - parser.add_option('-p', '--load-plugins', dest="load", action="store", + parser.add_option('-l', '--load-plugins', dest="load", action="store", metavar="PLUGINS", help="comma separated list of plugins to load") parser.add_option('-i', '--ignore-plugins', dest="ignore", action="store", metavar="PLUGINS", help="comma separated list of plugins to ignore") - parser.add_option('-l', '--list-plugins', dest='list', action='store_true', - help="list all the plugins in the plugin dir.") + parser.add_option('-p', '--pid-file', dest='pid', action='store', + metavar='FILE', help="specify pid file location") + + parser.add_option('-s', '--server-socket', dest='socket', action='store', + metavar='SOCKET', help="specify the daemon socket location") + + parser.add_option('-n', '--no-daemon', dest="daemon", + action="store_true", help="don't enter daemon mode.") (options, args) = parser.parse_args() - if len(args): - for arg in args: - error("unknown argument: %r" % arg) + # init like {start|stop|..} daemon control section. + daemon_controls = {'start': start, 'stop': stop, 'restart': restart, + 'list': list_plugins} + + if len(args) == 1: + action = args[0] + if action not in daemon_controls: + error('unknown action: %r' % action) + sys.exit(1) + + elif len(args) > 1: + error("too many arguments: %r" % args) + sys.exit(1) - raise ArgumentError + else: + action = 'start' + # parse other flags & options. if options.verbose: config['verbose'] = True - if options.plugin_dir: - plugin_dir = os.path.expandvars(options.plugin_dir) - if not os.path.isdir(plugin_dir): - error("%r is not a directory" % plugin_dir) - sys.exit(1) - - config['plugin_dir'] = plugin_dir - echo("changed plugin dir: %r" % plugin_dir) + if options.plugin_dirs: + plugin_dirs = map(str.strip, options.plugin_dirs.split(':')) + config['plugin_dirs'] = plugin_dirs + echo("plugin search dirs: %r" % plugin_dirs) if options.load and options.ignore: error("you can't load and ignore at the same time.") @@ -708,21 +818,16 @@ if __name__ == "__main__": echo('ignoring plugin(s): %s' % ', '.join(plugins_ignore)) + if options.pid: + config['pid_file'] = options.pid + echo("pid file location: %r" % config['pid_file']) - if options.list: - plugin_dir = os.path.expandvars(config['plugin_dir']) - if not os.path.isdir(plugin_dir): - error("not a directory: %r" % plugin_dir) - sys.exit(1) + if options.socket: + config['server_socket'] = options.socket + echo("daemon socket location: %s" % config['server_socket']) - dirlist = filter(lambda p: p.endswith('.py'), os.listdir(plugin_dir)) - print ', '.join([p[:-3] for p in dirlist]) + if options.daemon: + config['daemon_mode'] = False - else: - uzbl = UzblInstance() - if options.socket: - echo("listen from uzbl socket: %r" % options.socket) - uzbl.listen_from_uzbl_socket(options.socket) - - else: - uzbl.listen_from_fd(sys.stdin) + # Now {start|stop|...} + daemon_controls[action]() diff --git a/examples/data/uzbl/scripts/plugins/bind.py b/examples/data/uzbl/scripts/plugins/bind.py deleted file mode 100644 index 15f6f8e..0000000 --- a/examples/data/uzbl/scripts/plugins/bind.py +++ /dev/null @@ -1,374 +0,0 @@ -'''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 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: - uzbl.exec_handler(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 - uzbl.exec_handler(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) diff --git a/examples/data/uzbl/scripts/plugins/config.py b/examples/data/uzbl/scripts/plugins/config.py deleted file mode 100644 index 22803b4..0000000 --- a/examples/data/uzbl/scripts/plugins/config.py +++ /dev/null @@ -1,87 +0,0 @@ -import re -import types - -__export__ = ['set', 'get_config'] - -_VALIDSETKEY = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$").match -_TYPECONVERT = {'int': int, 'float': float, 'str': unicode} - -UZBLS = {} - - -def escape(value): - '''A real escaping function may be required.''' - - return unicode(value) - - -def get_config(uzbl): - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -def set(uzbl, key, value): - '''Sends a: "set key = value" command to the uzbl instance.''' - - if type(value) == types.BooleanType: - value = int(value) - - if not _VALIDSETKEY(key): - raise KeyError("%r" % key) - - value = escape(value) - if '\n' in value: - value = value.replace("\n", "\\n") - - uzbl.send('set %s = %s' % (key, value)) - - -def add_instance(uzbl, *args): - UZBLS[uzbl] = ConfigDict(uzbl) - - -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del uzbl - - -def get_config(uzbl): - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -class ConfigDict(dict): - def __init__(self, uzbl): - self._uzbl = uzbl - - def __setitem__(self, key, value): - '''Makes "config[key] = value" a wrapper for the set function.''' - - if key not in self or unicode(self[key]) != unicode(value): - set(self._uzbl, key, value) - - -def variable_set(uzbl, args): - config = get_config(uzbl) - - key, type, value = list(args.split(' ', 2) + ['',])[:3] - old = config[key] if key in config else None - value = _TYPECONVERT[type](value) - - dict.__setitem__(config, key, value) - - if old != value: - uzbl.event("CONFIG_CHANGED", key, value) - - -def init(uzbl): - - connects = {'VARIABLE_SET': variable_set, - 'INSTANCE_START': add_instance, - 'INSTANCE_EXIT': del_instance} - - uzbl.connect_dict(connects) diff --git a/examples/data/uzbl/scripts/plugins/keycmd.py b/examples/data/uzbl/scripts/plugins/keycmd.py deleted file mode 100644 index 3dd6f37..0000000 --- a/examples/data/uzbl/scripts/plugins/keycmd.py +++ /dev/null @@ -1,382 +0,0 @@ -import re - -# Map these functions/variables in the plugins namespace to the uzbl object. -__export__ = ['clear_keycmd', 'set_keycmd', 'set_cursor_pos', 'get_keylet'] - -# Regular expression compile cache. -_RE_CACHE = {} - -# Hold the keylets. -UZBLS = {} - -# Simple key names map. -_SIMPLEKEYS = { - 'Control': 'Ctrl', - 'ISO_Left_Tab': 'Shift-Tab', - 'space':'Space', -} - -# Keycmd format which includes the markup for the cursor. -KEYCMD_FORMAT = "%s%s%s" - - -def escape(str): - '''Prevent outgoing keycmd values from expanding inside the - status_format.''' - - if not str: - return '' - - for char in ['\\', '@']: - if char in str: - str = str.replace(char, '\\'+char) - - return "@[%s]@" % str - - -def get_regex(regex): - '''Compiling regular expressions is a very time consuming so return a - pre-compiled regex match object if possible.''' - - if regex not in _RE_CACHE: - _RE_CACHE[regex] = re.compile(regex).match - - return _RE_CACHE[regex] - - -class Keylet(object): - '''Small per-instance object that tracks all the keys held and characters - typed.''' - - def __init__(self): - self.cmd = '' - self.cursor = 0 - self.held = [] - - # to_string() string building cache. - self._to_string = None - - self.modcmd = False - self.wasmod = False - - def __repr__(self): - return '' % self.to_string() - - - def to_string(self): - '''Return a string representation of the keys held and pressed that - have been recorded.''' - - if self._to_string is not None: - # Return cached keycmd string. - return self._to_string - - if not self.held: - self._to_string = self.cmd - - else: - self._to_string = ''.join(['<%s>' % key for key in self.held]) - if self.cmd: - self._to_string += self.cmd - - return self._to_string - - - def match(self, regex): - '''See if the keycmd string matches the given regex.''' - - return bool(get_regex(regex)(self.to_string())) - - -def make_simple(key): - '''Make some obscure names for some keys friendlier.''' - - # Remove left-right discrimination. - if key.endswith('_L') or key.endswith('_R'): - key = key[:-2] - - if key in _SIMPLEKEYS: - key = _SIMPLEKEYS[key] - - return key - - -def add_instance(uzbl, *args): - '''Create the Keylet object for this uzbl instance.''' - - UZBLS[uzbl] = Keylet() - - -def del_instance(uzbl, *args): - '''Delete the Keylet object for this uzbl instance.''' - - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_keylet(uzbl): - '''Return the corresponding keylet for this uzbl instance.''' - - # Startup events are not correctly captured and sent over the uzbl socket - # yet so this line is needed because the INSTANCE_START event is lost. - if uzbl not in UZBLS: - add_instance(uzbl) - - keylet = UZBLS[uzbl] - keylet._to_string = None - return keylet - - -def clear_keycmd(uzbl): - '''Clear the keycmd for this uzbl instance.''' - - k = get_keylet(uzbl) - k.cmd = '' - k.cursor = 0 - k._to_string = None - - if k.modcmd: - k.wasmod = True - - k.modcmd = False - config = uzbl.get_config() - if 'keycmd' not in config or config['keycmd'] != '': - config['keycmd'] = '' - - uzbl.event('KEYCMD_CLEAR') - - -def update_event(uzbl, k): - '''Raise keycmd & modcmd update events.''' - - config = uzbl.get_config() - if k.modcmd: - keycmd = k.to_string() - uzbl.event('MODCMD_UPDATE', k) - if keycmd != k.to_string(): - return - - if 'modcmd_updates' in config and config['modcmd_updates'] != '1': - return - - return uzbl.set('keycmd', escape(keycmd)) - - if 'keycmd_events' in config and config['keycmd_events'] != '1': - return - - keycmd = k.cmd - uzbl.event('KEYCMD_UPDATE', k) - if keycmd != k.cmd: - return - - if not k.cmd: - return uzbl.set('keycmd', '') - - # Generate the pango markup for the cursor in the keycmd. - if k.cursor < len(k.cmd): - cursor = k.cmd[k.cursor] - - else: - cursor = ' ' - - chunks = map(escape, [k.cmd[:k.cursor], cursor, k.cmd[k.cursor+1:]]) - uzbl.set('keycmd', KEYCMD_FORMAT % tuple(chunks)) - - -def key_press(uzbl, key): - '''Handle KEY_PRESS events. Things done by this function include: - - 1. Ignore all shift key presses (shift can be detected by capital chars) - 2. Re-enable modcmd var if the user presses another key with at least one - modkey still held from the previous modcmd (I.e. +t, clear & - +o without having to re-press ) - 3. In non-modcmd mode: - a. BackSpace deletes the character before the cursor position. - b. Delete deletes the character at the cursor position. - c. End moves the cursor to the end of the keycmd. - d. Home moves the cursor to the beginning of the keycmd. - e. Return raises a KEYCMD_EXEC event then clears the keycmd. - f. Escape clears the keycmd. - 4. If keycmd and held keys are both empty/null and a modkey was pressed - set modcmd mode. - 5. If in modcmd mode only mod keys are added to the held keys list. - 6. Keycmd is updated and events raised if anything is changed.''' - - if key.startswith('Shift_'): - return - - if len(key) > 1: - key = make_simple(key) - - k = get_keylet(uzbl) - cmdmod = False - if k.held and k.wasmod: - k.modcmd = True - k.wasmod = False - cmdmod = True - - if k.cmd and key == 'Space': - k.cmd = "%s %s" % (k.cmd[:k.cursor], k.cmd[k.cursor:]) - k.cursor += 1 - cmdmod = True - - elif not k.modcmd and k.cmd and key in ['BackSpace', 'Delete']: - if key == 'BackSpace' and k.cursor > 0: - k.cursor -= 1 - k.cmd = k.cmd[:k.cursor] + k.cmd[k.cursor+1:] - - elif key == 'Delete': - cmd = k.cmd - k.cmd = k.cmd[:k.cursor] + k.cmd[k.cursor+1:] - if k.cmd != cmd: - cmdmod = True - - if not k.cmd: - clear_keycmd(uzbl) - - elif key == 'BackSpace': - cmdmod = True - - elif not k.modcmd and key == 'Return': - if k.cmd: - uzbl.event('KEYCMD_EXEC', k) - - clear_keycmd(uzbl) - - elif not k.modcmd and key == 'Escape': - clear_keycmd(uzbl) - - elif not k.modcmd and k.cmd and key == 'Left': - if k.cursor > 0: - k.cursor -= 1 - cmdmod = True - - elif not k.modcmd and k.cmd and key == 'Right': - if k.cursor < len(k.cmd): - k.cursor += 1 - cmdmod = True - - elif not k.modcmd and k.cmd and key == 'End': - if k.cursor != len(k.cmd): - k.cursor = len(k.cmd) - cmdmod = True - - elif not k.modcmd and k.cmd and key == 'Home': - if k.cursor: - k.cursor = 0 - cmdmod = True - - elif not k.held and not k.cmd and len(key) > 1: - k.modcmd = True - k.held.append(key) - cmdmod = True - - elif k.modcmd: - cmdmod = True - if len(key) > 1: - if key == 'Shift-Tab' and 'Tab' in k.held: - k.held.remove('Tab') - - if key not in k.held: - k.held.append(key) - k.held.sort() - - else: - k.cmd = "%s%s%s" % (k.cmd[:k.cursor], key, k.cmd[k.cursor:]) - k.cursor += 1 - - else: - config = uzbl.get_config() - if 'keycmd_events' not in config or config['keycmd_events'] == '1': - if len(key) == 1: - cmdmod = True - k.cmd = "%s%s%s" % (k.cmd[:k.cursor], key, k.cmd[k.cursor:]) - k.cursor += 1 - - elif k.cmd: - cmdmod = True - k.cmd = '' - k.cursor = 0 - - if cmdmod: - update_event(uzbl, k) - - -def key_release(uzbl, key): - '''Respond to KEY_RELEASE event. Things done by this function include: - - 1. Remove the key from the keylet held list. - 2. If the key removed was a mod key and it was in a mod-command then - raise a MODCMD_EXEC event then clear the keycmd. - 3. Stop trying to restore mod-command status with wasmod if both the - keycmd and held list are empty/null. - 4. Update the keycmd uzbl variable if anything changed.''' - - if len(key) > 1: - key = make_simple(key) - - k = get_keylet(uzbl) - - cmdmod = False - if key in ['Shift', 'Tab'] and 'Shift-Tab' in k.held: - key = 'Shift-Tab' - - elif key in ['Shift', 'Alt'] and 'Meta' in k.held: - key = 'Meta' - - if key in k.held: - if k.modcmd: - uzbl.event('MODCMD_EXEC', k) - - k.held.remove(key) - clear_keycmd(uzbl) - - if not k.held and not k.cmd and k.wasmod: - k.wasmod = False - - if cmdmod: - update_event(uzbl, k) - - -def set_keycmd(uzbl, keycmd): - '''Allow setting of the keycmd externally.''' - - k = get_keylet(uzbl) - k.wasmod = k.modcmd = False - k._to_string = None - k.cmd = keycmd - k.cursor = len(keycmd) - update_event(uzbl, k) - - -def set_cursor_pos(uzbl, index): - '''Allow setting of the cursor position externally. Supports negative - indexing.''' - - cursor = int(index.strip()) - k = get_keylet(uzbl) - - if cursor < 0: - cursor = len(k.cmd) + cursor - - if cursor < 0: - cursor = 0 - - if cursor > len(k.cmd): - cursor = len(k.cmd) - - k.cursor = cursor - update_event(uzbl, k) - - -def init(uzbl): - '''Connect handlers to uzbl events.''' - - connects = {'INSTANCE_START': add_instance, - 'INSTANCE_EXIT': del_instance, - 'KEY_PRESS': key_press, - 'KEY_RELEASE': key_release, - 'SET_KEYCMD': set_keycmd, - 'SET_CURSOR_POS': set_cursor_pos} - - uzbl.connect_dict(connects) diff --git a/examples/data/uzbl/scripts/plugins/mode.py b/examples/data/uzbl/scripts/plugins/mode.py deleted file mode 100644 index ad0d9a8..0000000 --- a/examples/data/uzbl/scripts/plugins/mode.py +++ /dev/null @@ -1,159 +0,0 @@ -import sys -import re - -__export__ = ['set_mode', 'get_mode'] - -UZBLS = {} - -DEFAULTS = { - 'mode': '', - 'default': '', - 'modes': { - 'insert': { - 'forward_keys': True, - 'keycmd_events': False, - 'modcmd_updates': False, - 'indicator': 'I'}, - 'command': { - 'forward_keys': False, - 'keycmd_events': True, - 'modcmd_updates': True, - 'indicator': 'C'}}} - -_RE_FINDSPACES = re.compile("\s+") - - -def error(msg): - sys.stderr.write("mode plugin: error: %s\n" % msg) - - -def add_instance(uzbl, *args): - UZBLS[uzbl] = dict(DEFAULTS) - - -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_mode_dict(uzbl): - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -def get_mode_config(uzbl, mode): - modes = get_mode_dict(uzbl)['modes'] - if mode not in modes: - modes[mode] = {} - - return modes[mode] - - -def get_mode(uzbl): - return get_mode_dict(uzbl)['mode'] - - -def key_press(uzbl, key): - if key != "Escape": - return - - set_mode(uzbl) - - -def set_mode(uzbl, mode=None): - mode_dict = get_mode_dict(uzbl) - if mode is None: - if not mode_dict['default']: - return error("no default mode to fallback on") - - mode = mode_dict['default'] - - config = uzbl.get_config() - if 'mode' not in config or config['mode'] != mode: - config['mode'] = mode - - mode_dict['mode'] = mode - mode_config = get_mode_config(uzbl, mode) - - for (key, value) in mode_config.items(): - if key not in config: - config[key] = value - - elif config[key] != value: - config[key] = value - - if 'mode_indicator' not in mode_config: - config['mode_indicator'] = mode - - uzbl.clear_keycmd() - uzbl.send('update_gui') - uzbl.event("MODE_CHANGED", mode) - - -def config_changed(uzbl, key, value): - if key == 'default_mode': - mode_dict = get_mode_dict(uzbl) - mode_dict['default'] = value - if value and not mode_dict['mode']: - set_mode(uzbl, value) - - elif key == 'mode': - if not value: - value = None - - set_mode(uzbl, value) - - -def mode_config(uzbl, args): - - split = map(unicode.strip, _RE_FINDSPACES.split(args.lstrip(), 1)) - if len(split) != 2: - return error("invalid MODE_CONFIG syntax: %r" % args) - - mode, set = split - split = map(unicode.strip, set.split('=', 1)) - if len(split) != 2: - return error("invalid MODE_CONFIG set command: %r" % args) - - key, value = split - mode_config = get_mode_config(uzbl, mode) - mode_config[key] = value - - if get_mode(uzbl) == mode: - uzbl.set(key, value) - - -def load_reset(uzbl, *args): - config = uzbl.get_config() - if 'reset_on_commit' not in config or config['reset_on_commit'] == '1': - set_mode(uzbl) - - -def toggle_modes(uzbl, modes): - - modelist = [s.strip() for s in modes.split(' ') if s] - if not len(modelist): - return error("no modes specified to toggle") - - mode_dict = get_mode_dict(uzbl) - oldmode = mode_dict['mode'] - if oldmode not in modelist: - return set_mode(uzbl, modelist[0]) - - newmode = modelist[(modelist.index(oldmode)+1) % len(modelist)] - set_mode(uzbl, newmode) - - -def init(uzbl): - - connects = {'CONFIG_CHANGED': config_changed, - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, - 'KEY_PRESS': key_press, - 'MODE_CONFIG': mode_config, - 'LOAD_START': load_reset, - 'TOGGLE_MODES': toggle_modes} - - uzbl.connect_dict(connects) diff --git a/examples/data/uzbl/scripts/plugins/on_event.py b/examples/data/uzbl/scripts/plugins/on_event.py deleted file mode 100644 index 242f9b0..0000000 --- a/examples/data/uzbl/scripts/plugins/on_event.py +++ /dev/null @@ -1,117 +0,0 @@ -'''Plugin provides arbitrary binding of uzbl events to uzbl commands. - -Formatting options: - %@ = space separated string of the arguments - %1 = argument 1 - %2 = argument 2 - %n = argument n - -Usage: - request ON_EVENT LINK_HOVER set selected_uri = $1 - --> LINK_HOVER http://uzbl.org/ - <-- set selected_uri = http://uzbl.org/ - - request ON_EVENT CONFIG_CHANGED print Config changed: %1 = %2 - --> CONFIG_CHANGED selected_uri http://uzbl.org/ - <-- print Config changed: selected_uri = http://uzbl.org/ -''' - -import sys -import re -from event_manager import config - -__export__ = ['get_on_events', 'on_event'] - -UZBLS = {} - -def echo(msg): - if config['verbose']: - print 'on_event plugin:', msg - - -def error(msg): - sys.stderr.write('on_event plugin: error: %s\n' % msg) - - -def add_instance(uzbl, *args): - UZBLS[uzbl] = {} - - -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_on_events(uzbl): - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -def expand(cmd, args): - '''Replaces "%@ %1 %2 %3..." with " ...".''' - - if '%@' in cmd: - cmd = cmd.replace('%@', ' '.join(map(unicode, args))) - - for (index, arg) in enumerate(args): - index += 1 - if '%%%d' % index in cmd: - cmd = cmd.replace('%%%d' % index, unicode(arg)) - - return cmd - - -def event_handler(uzbl, *args, **kargs): - '''This function handles all the events being watched by various - on_event definitions and responds accordingly.''' - - events = get_on_events(uzbl) - event = kargs['on_event'] - if event not in events: - return - - commands = events[event] - for cmd in commands: - cmd = expand(cmd, args) - uzbl.send(cmd) - - -def on_event(uzbl, event, cmd): - '''Add a new event to watch and respond to.''' - - event = event.upper() - events = get_on_events(uzbl) - if event not in events: - uzbl.connect(event, event_handler, on_event=event) - events[event] = [] - - cmds = events[event] - if cmd not in cmds: - cmds.append(cmd) - - -def parse_on_event(uzbl, args): - '''Parse ON_EVENT events and pass them to the on_event function. - - Syntax: "event ON_EVENT commands".''' - - if not args: - return error("missing on_event arguments") - - split = args.split(' ', 1) - if len(split) != 2: - return error("invalid ON_EVENT syntax: %r" % args) - - event, cmd = split - on_event(uzbl, event, cmd) - - -def init(uzbl): - - connects = {'ON_EVENT': parse_on_event, - 'INSTANCE_START': add_instance, - 'INSTANCE_EXIT': del_instance} - - uzbl.connect_dict(connects) diff --git a/examples/data/uzbl/scripts/plugins/plugin_template.py b/examples/data/uzbl/scripts/plugins/plugin_template.py deleted file mode 100644 index 03cb748..0000000 --- a/examples/data/uzbl/scripts/plugins/plugin_template.py +++ /dev/null @@ -1,75 +0,0 @@ -'''Plugin template.''' - -# A list of functions this plugin exports to be used via uzbl object. -__export__ = ['myplugin_function',] - -# Holds the per-instance data dict. -UZBLS = {} - -# The default instance dict. -DEFAULTS = {} - - -def add_instance(uzbl, *args): - '''Add a new instance with default config options.''' - - UZBLS[uzbl] = dict(DEFAULTS) - - -def del_instance(uzbl, *args): - '''Delete data stored for an instance.''' - - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_myplugin_dict(uzbl): - '''Get data stored for an instance.''' - - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -def myplugin_function(uzbl, *args, **kargs): - '''Custom plugin function which is exported by the __export__ list at the - top of the file for use by other functions/callbacks.''' - - print "My plugin function arguments:", args, kargs - - # Get the per-instance data object. - data = get_myplugin_dict(uzbl) - - # Function logic goes here. - - -def myplugin_event_parser(uzbl, args): - '''Parses MYPLUGIN_EVENT raised by uzbl or another plugin.''' - - print "Got MYPLUGIN_EVENT with arguments: %r" % args - - # Parsing logic goes here. - - -def init(uzbl): - '''The main function of the plugin which is used to attach all the event - hooks that are going to be used throughout the plugins life. This function - is called each time a UzblInstance() object is created in the event - manager.''' - - # Make a dictionary comprising of {"EVENT_NAME": handler, ..} to the event - # handler stack: - connects = { - 'INSTANCE_START': add_instance, - 'INSTANCE_EXIT': del_instance, - 'MYPLUGIN_EVENT': myplugin_event_parser, - } - - # And connect the dicts event handlers to the handler stack. - uzbl.connect_dict(connects) - - # Or connect a handler to an event manually and supply additional optional - # arguments: - - #uzbl.connect("MYOTHER_EVENT", myother_event_parser, True, limit=20) diff --git a/examples/data/uzbl/scripts/plugins/progress_bar.py b/examples/data/uzbl/scripts/plugins/progress_bar.py deleted file mode 100644 index b6fcb1b..0000000 --- a/examples/data/uzbl/scripts/plugins/progress_bar.py +++ /dev/null @@ -1,158 +0,0 @@ -import sys - -UZBLS = {} - -DEFAULTS = {'width': 8, - 'done': '=', - 'pending': '.', - 'format': '[%d%a%p]%c', - 'spinner': '-\\|/', - 'sprites': 'loading', - 'updates': 0, - 'progress': 100} - - -def error(msg): - sys.stderr.write("progress_bar plugin: error: %s\n" % msg) - - -def add_instance(uzbl, *args): - UZBLS[uzbl] = dict(DEFAULTS) - - -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_progress_config(uzbl): - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -def update_progress(uzbl, prog=None): - '''Updates the progress_format variable on LOAD_PROGRESS update. - - The current substitution options are: - %d = done char * done - %p = pending char * remaining - %c = percent done - %i = int done - %s = -\|/ spinner - %t = percent pending - %o = int pending - %r = sprites - ''' - - prog_config = get_progress_config(uzbl) - config = uzbl.get_config() - - if prog is None: - prog = prog_config['progress'] - - else: - prog = int(prog) - prog_config['progress'] = prog - - prog_config['updates'] += 1 - format = prog_config['format'] - width = prog_config['width'] - - # Inflate the done and pending bars to stop the progress bar - # jumping around. - if '%c' in format or '%i' in format: - count = format.count('%c') + format.count('%i') - width += (3-len(str(prog))) * count - - if '%t' in format or '%o' in format: - count = format.count('%t') + format.count('%o') - width += (3-len(str(100-prog))) * count - - done = int(((prog/100.0)*width)+0.5) - pending = width - done - - if '%d' in format: - format = format.replace('%d', prog_config['done']*done) - - if '%p' in format: - format = format.replace('%p', prog_config['pending']*pending) - - if '%c' in format: - format = format.replace('%c', '%d%%' % prog) - - if '%i' in format: - format = format.replace('%i', '%d' % prog) - - if '%t' in format: - format = format.replace('%t', '%d%%' % (100-prog)) - - if '%o' in format: - format = format.replace('%o', '%d' % (100-prog)) - - if '%s' in format: - spinner = prog_config['spinner'] - spin = '-' if not spinner else spinner - index = 0 if prog == 100 else prog_config['updates'] % len(spin) - char = '\\\\' if spin[index] == '\\' else spin[index] - format = format.replace('%s', char) - - if '%r' in format: - sprites = prog_config['sprites'] - sprites = '-' if not sprites else sprites - index = int(((prog/100.0)*len(sprites))+0.5)-1 - sprite = '\\\\' if sprites[index] == '\\' else sprites[index] - format = format.replace('%r', sprite) - - if 'progress_format' not in config or config['progress_format'] != format: - config['progress_format'] = format - - -def progress_config(uzbl, args): - '''Parse PROGRESS_CONFIG events from the uzbl instance. - - Syntax: event PROGRESS_CONFIG = - ''' - - split = args.split('=', 1) - if len(split) != 2: - return error("invalid syntax: %r" % args) - - key, value = map(unicode.strip, split) - prog_config = get_progress_config(uzbl) - - if key not in prog_config: - return error("key error: %r" % args) - - if type(prog_config[key]) == type(1): - try: - value = int(value) - - except: - return error("invalid type: %r" % args) - - elif not value: - value = ' ' - - prog_config[key] = value - update_progress(uzbl) - - -def reset_progress(uzbl, args): - '''Reset the spinner counter, reset the progress int and re-draw the - progress bar on LOAD_COMMIT.''' - - prog_dict = get_progress_config(uzbl) - prog_dict['updates'] = prog_dict['progress'] = 0 - update_progress(uzbl) - - -def init(uzbl): - connects = {'LOAD_PROGRESS': update_progress, - 'INSTANCE_START': add_instance, - 'INSTANCE_EXIT': del_instance, - 'PROGRESS_CONFIG': progress_config, - 'LOAD_COMMIT': reset_progress} - - uzbl.connect_dict(connects) -- cgit v1.2.3