aboutsummaryrefslogtreecommitdiffhomepage
path: root/examples/data/uzbl/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'examples/data/uzbl/scripts')
-rwxr-xr-xexamples/data/uzbl/scripts/event_manager.py134
-rw-r--r--examples/data/uzbl/scripts/plugins/bind.py246
-rw-r--r--examples/data/uzbl/scripts/plugins/config.py87
-rw-r--r--examples/data/uzbl/scripts/plugins/keycmd.py105
-rw-r--r--examples/data/uzbl/scripts/plugins/mode.py159
-rw-r--r--examples/data/uzbl/scripts/plugins/on_event.py117
-rw-r--r--examples/data/uzbl/scripts/plugins/plugin_template.py75
-rw-r--r--examples/data/uzbl/scripts/plugins/progress_bar.py158
8 files changed, 887 insertions, 194 deletions
diff --git a/examples/data/uzbl/scripts/event_manager.py b/examples/data/uzbl/scripts/event_manager.py
index 018c066..2e84ded 100755
--- a/examples/data/uzbl/scripts/event_manager.py
+++ b/examples/data/uzbl/scripts/event_manager.py
@@ -85,9 +85,8 @@ config = {
# Define some globals.
-_VALIDSETKEY = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$").match
_SCRIPTNAME = os.path.basename(sys.argv[0])
-_TYPECONVERT = {'int': int, 'float': float, 'str': str}
+_RE_FINDSPACES = re.compile("\s+")
def echo(msg):
'''Prints only if the verbose flag has been set.'''
@@ -277,35 +276,11 @@ class UzblInstance(object):
# Singleton plugin manager.
plugins = None
- # Internal uzbl config dict.
- class ConfigDict(dict):
- def __init__(self, setcmd):
- self._setcmd = setcmd
-
- def __setitem__(self, key, value):
- '''Updates the config dict and relays any changes back to the
- uzbl instance via the set function.'''
-
- if type(value) == types.BooleanType:
- value = int(value)
-
- if key in self.keys() and type(value) != type(self[key]):
- raise TypeError("%r for %r" % (type(value), key))
-
- 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.'''
# Hold functions exported by plugins.
self._exports = {}
- self._config = self.ConfigDict(self.set)
self._running = None
self._buffer = ''
@@ -338,14 +313,6 @@ class UzblInstance(object):
return object.__getattribute__(self, name)
- def _get_config(self):
- '''Return the uzbl config dictionary.'''
-
- return self._config
-
- config = property(_get_config)
-
-
def _init_plugins(self):
'''Call the init() function in every plugin and expose all exposable
functions in the plugins root namespace.'''
@@ -409,6 +376,17 @@ class UzblInstance(object):
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.'''
@@ -417,8 +395,9 @@ class UzblInstance(object):
h = open(self.uzbl_fifo, 'w')
while len(self._fifo_cmd_queue):
msg = self._fifo_cmd_queue.pop(0)
- print "Sending via fifo: %r" % msg
- h.write("%s\n" % msg)
+ print '<-- %s' % msg
+ h.write(("%s\n" % msg).encode('utf-8'))
+
h.close()
if len(self._socket_cmd_queue) and self.uzbl_socket:
@@ -428,8 +407,8 @@ class UzblInstance(object):
if self._socket:
while len(self._socket_cmd_queue):
msg = self._socket_cmd_queue.pop(0)
- print "Sending via socket: %r" % msg
- self._socket.send("%s\n" % msg)
+ print '<-- %s' % msg
+ self._socket.send(("%s\n" % msg).encode('utf-8'))
def _send_fifo(self, msg):
@@ -456,10 +435,21 @@ class UzblInstance(object):
handler = Handler(event, handler, *args, **kargs)
self._handlers[event].append(handler)
- print "New event handler:", handler
+ print handler
return handler
+ def connect_dict(self, connect_dict):
+ '''Connect a dictionary comprising of {"EVENT_NAME": handler, ..} to
+ the event handler stack.
+
+ If you need to supply args or kargs to an event use the normal connect
+ function.'''
+
+ for (event, handler) in connect_dict.items():
+ self.connect(event, handler)
+
+
def remove_by_id(self, hid):
'''Remove connected event handler by unique handler id.'''
@@ -487,21 +477,6 @@ class UzblInstance(object):
echo('unable to find & remove handler: %r' % handler)
- def set(self, key, value):
- '''Sets key "key" with value "value" in the uzbl instance.'''
-
- # TODO: Make a real escaping function.
- escape = str
-
- if not _VALIDSETKEY(key):
- raise KeyError("%r" % key)
-
- if '\n' in value:
- raise ValueError("invalid character: \\n")
-
- self.send('set %s = %s' % (key, escape(value)))
-
-
def listen_from_fd(self, fd):
'''Polls for event messages from fd.'''
@@ -535,7 +510,7 @@ class UzblInstance(object):
if not msg or msg[0] != "EVENT":
# Not an event message
- print raw.rstrip()
+ print "---", raw.rstrip()
return
event, args = msg[1], msg[3]
@@ -572,7 +547,7 @@ class UzblInstance(object):
def read_from_uzbl_socket(self):
'''Reads event messages from a uzbl socket.'''
- raw = self._socket.recv(1024)
+ raw = unicode(self._socket.recv(8192), 'utf-8', 'ignore')
if not raw:
# Read null byte
self._running = False
@@ -587,13 +562,16 @@ class UzblInstance(object):
if not msg:
continue
- cmd = msg.strip().split(' ', 3)
+ cmd = _RE_FINDSPACES.split(msg, 3)
if not cmd or cmd[0] != "EVENT":
# Not an event message
print msg.rstrip()
continue
- event, args = cmd[1], cmd[3]
+ if len(cmd) < 4:
+ cmd.append('')
+
+ event, args = cmd[2], cmd[3]
try:
self.handle_event(event, args)
@@ -605,19 +583,7 @@ class UzblInstance(object):
def handle_event(self, event, args):
'''Handle uzbl events internally before dispatch.'''
- # Silence _printing_ of geo events while still debugging.
- if event != "GEOMETRY_CHANGED":
- print event, args
-
- if event == 'VARIABLE_SET':
- l = args.split(' ', 2)
- if len(l) == 2:
- l.append("")
-
- key, type, value = l
- dict.__setitem__(self._config, key, _TYPECONVERT[type](value))
-
- elif event == 'FIFO_SET':
+ if event == 'FIFO_SET':
self.uzbl_fifo = args
self._flush()
@@ -626,13 +592,15 @@ class UzblInstance(object):
self._init_uzbl_socket(args)
self._flush()
- elif event == 'SHUTDOWN':
+ elif event == 'INSTANCE_EXIT':
+ self._close_socket()
+ self._running = False
for (name, plugin) in self.plugins.items():
if hasattr(plugin, "cleanup"):
plugin.cleanup(uzbl)
# Now handle the event "publically".
- self.dispatch_event(event, args)
+ self.event(event, args)
def exec_handler(self, handler, *args, **kargs):
@@ -660,24 +628,12 @@ class UzblInstance(object):
uzbl.send(command)
- def dispatch_event(self, event, args):
- '''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:
- for handler in self._handlers[event]:
- try:
- self.exec_handler(handler, args)
-
- except:
- #print_exc()
- raise
-
-
def event(self, event, *args, **kargs):
'''Raise a custom event.'''
- print "Got custom event:", event, args, kargs
+ # Silence _printing_ of geo events while still debugging.
+ if event != "GEOMETRY_CHANGED":
+ print "--> %s %s %s" % (event, args, '' if not kargs else kargs)
if event in self._handlers:
for handler in self._handlers[event]:
@@ -701,7 +657,7 @@ if __name__ == "__main__":
help="print verbose output.")
parser.add_option('-d', '--plugin-dir', dest='plugin_dir', action="store",
- metavar="FILE", help="change plugin directory.")
+ metavar="DIR", help="change plugin directory.")
parser.add_option('-p', '--load-plugins', dest="load", action="store",
metavar="PLUGINS", help="comma separated list of plugins to load")
diff --git a/examples/data/uzbl/scripts/plugins/bind.py b/examples/data/uzbl/scripts/plugins/bind.py
index 98f7d4c..15f6f8e 100644
--- a/examples/data/uzbl/scripts/plugins/bind.py
+++ b/examples/data/uzbl/scripts/plugins/bind.py
@@ -17,19 +17,27 @@ from event_manager import config, counter, iscallable, isiterable
__export__ = ['bind', 'del_bind', 'del_bind_by_glob', 'get_binds']
# Hold the bind lists per uzbl instance.
-_UZBLS = {}
+UZBLS = {}
# Commonly used regular expressions.
-starts_with_mod = re.compile('^<([A-Za-z0-9-_]+|.)>')
+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 "plugin: bind:", msg
+ print 'bind plugin:', msg
def error(msg):
- sys.stderr.write("plugin: bind: error: %s" % msg)
+ sys.stderr.write('bind plugin: error: %s\n' % msg)
def ismodbind(glob):
@@ -52,32 +60,49 @@ def sort_mods(glob):
mods.append(glob[:end])
glob = glob[end:]
- return "%s%s" % (''.join(sorted(mods)), glob)
+ return '%s%s' % (''.join(sorted(mods)), glob)
def add_instance(uzbl, *args):
- _UZBLS[uzbl] = []
-
- # Until the inital event messages over socket bug is resolved put your
- # bind events here:
- uzbl.event("BIND", "ZZ = exit")
- uzbl.event("BIND", "<Ctrl>q = exit")
- uzbl.event("BIND", "<Ctrl><Alt>h = uri http://uzbl.org/")
- uzbl.event("BIND", "rr = uri http://reddit.com")
+ UZBLS[uzbl] = {'binds': [], 'depth': 0, 'filter': [],
+ 'args': [], 'last_mode': ''}
def del_instance(uzbl, *args):
- if uzbl in _UZBLS:
- del _UZBLS[uzbl]
+ 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.'''
- if uzbl not in _UZBLS:
- add_instance(uzbl)
+ return get_bind_dict(uzbl)['binds']
+
+
+def get_stack_depth(uzbl):
+ '''Return the stack for the uzbl instance.'''
- return _UZBLS[uzbl]
+ 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):
@@ -86,7 +111,7 @@ def del_bind(uzbl, bind):
binds = get_binds(uzbl)
if bind in binds:
binds.remove(bind)
- uzbl.event("DELETED_BIND", bind)
+ uzbl.event('DELETED_BIND', bind)
return True
return False
@@ -99,7 +124,7 @@ def del_bind_by_glob(uzbl, glob):
for bind in list(binds):
if bind.glob == glob:
binds.remove(bind)
- uzbl.event("DELETED_BIND", bind)
+ uzbl.event('DELETED_BIND', bind)
return True
return False
@@ -121,7 +146,7 @@ class Bind(object):
self.kargs = kargs
elif kargs:
- raise ArgumentError("cannot supply kargs for uzbl commands")
+ raise ArgumentError('cannot supply kargs for uzbl commands')
elif isiterable(handler):
self.commands = handler
@@ -132,39 +157,54 @@ class Bind(object):
self.glob = glob
self.bid = self.nextbid()
- # Is the binding a MODCMD or KEYCMD.
- self.mod_bind = ismodbind(glob)
+ self.split = split = find_prompts(glob)
+ self.prompts = split[1::2]
- # Execute the command on UPDATES or EXEC's.
- self.on_exec = True if glob.endswith('_') else False
+ # Check that there is nothing like: fl*<int:>*
+ for glob in split[:-1:2]:
+ if glob.endswith('*'):
+ msg = "token '*' not at the end of a prompt bind: %r" % split
+ raise BindParseError(msg)
- if glob[-1] in ['*', '_']:
- self.has_args = True
- glob = glob[:-1]
+ # Check that there is nothing like: fl<prompt1:><prompt2:>_
+ for glob in split[2::2]:
+ if not glob:
+ msg = 'found null segment after first prompt: %r' % split
+ raise BindParseError(msg)
- else:
- self.has_args = False
+ self.stack = []
+
+ for glob in split[::2]:
+ # Is the binding a MODCMD or KEYCMD:
+ mod_cmd = ismodbind(glob)
- self.match = 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]
+ args = ['glob=%r' % self.glob, 'bid=%d' % self.bid]
if self.callable:
- args.append("function=%r" % self.function)
+ args.append('function=%r' % self.function)
if self.args:
- args.append("args=%r" % self.args)
+ args.append('args=%r' % self.args)
if self.kargs:
- args.append("kargs=%r" % 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))
+ args.append('command%s=%r' % ('s' if cmdlen-1 else '', cmds))
- return "<Bind(%s)>" % ', '.join(args)
+ return '<Bind(%s)>' % ', '.join(args)
def bind(uzbl, glob, handler, *args, **kargs):
@@ -180,81 +220,155 @@ def bind(uzbl, glob, handler, *args, **kargs):
bind = Bind(glob, handler, *args, **kargs)
binds.append(bind)
+ print bind
uzbl.event('ADDED_BIND', bind)
def parse_bind_event(uzbl, args):
- '''Parse "event BIND fl* = js follownums.js" commands.'''
+ '''Break "event BIND fl* = js follownums.js" into (glob, command).'''
+
+ if not args:
+ return error('missing bind arguments')
- if len(args.split('=', 1)) != 2:
- error('invalid bind format: %r' % args)
+ split = map(unicode.strip, args.split('=', 1))
+ if len(split) != 2:
+ return error('missing "=" in bind definition: %r' % args)
- glob, command = map(str.strip, args.split('=', 1))
+ glob, command = split
bind(uzbl, glob, command)
-def match_and_exec(uzbl, bind, keylet):
+def set_stack_mode(uzbl, prompt):
+ if uzbl.get_mode() != 'stack':
+ uzbl.set_mode('stack')
- keycmd = keylet.to_string()
- if bind.has_args:
- if not keycmd.startswith(bind.match):
+ 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(bind.match):],]
+ args = [keycmd[len(glob):],]
- elif keycmd != bind.match:
+ elif keycmd != glob:
+ filter_bind(uzbl, bind_dict, bind)
return False
else:
args = []
- uzbl.exec_handler(bind, *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
- if not bind.has_args:
- uzbl.clear_keycmd()
+ args = bind_dict['args'] + args
+ uzbl.exec_handler(bind, *args)
+ if on_exec:
+ uzbl.set_mode()
return True
def keycmd_update(uzbl, keylet):
- for bind in get_binds(uzbl):
- if bind.mod_bind or bind.on_exec:
+ 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, keylet)
+ match_and_exec(uzbl, bind, depth, keycmd)
def keycmd_exec(uzbl, keylet):
- for bind in get_binds(uzbl):
- if bind.mod_bind or not bind.on_exec:
+ 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, keylet)
+ match_and_exec(uzbl, bind, depth, keycmd)
def modcmd_update(uzbl, keylet):
- for bind in get_binds(uzbl):
- if not bind.mod_bind or bind.on_exec:
+ 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, keylet)
+ match_and_exec(uzbl, bind, depth, keycmd)
def modcmd_exec(uzbl, keylet):
- for bind in get_binds(uzbl):
- if not bind.mod_bind or not bind.on_exec:
+ 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, keylet)
+ 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}
+ 'MODCMD_EXEC': modcmd_exec,
+ 'MODE_CHANGED': clear_stack}
- for (event, handler) in connects.items():
- uzbl.connect(event, handler)
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/config.py b/examples/data/uzbl/scripts/plugins/config.py
new file mode 100644
index 0000000..22803b4
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/config.py
@@ -0,0 +1,87 @@
+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
index 218681c..96e61b4 100644
--- a/examples/data/uzbl/scripts/plugins/keycmd.py
+++ b/examples/data/uzbl/scripts/plugins/keycmd.py
@@ -7,7 +7,7 @@ __export__ = ['clear_keycmd',]
_RE_CACHE = {}
# Hold the keylets.
-_UZBLS = {}
+UZBLS = {}
# Simple key names map.
_SIMPLEKEYS = {
@@ -17,6 +17,17 @@ _SIMPLEKEYS = {
}
+def keycmd_escape(keycmd):
+ '''Prevent outgoing keycmd values from expanding inside the
+ status_format.'''
+
+ for char in ['\\', '@']:
+ if char in keycmd:
+ keycmd = keycmd.replace(char, '\\'+char)
+
+ return keycmd
+
+
def get_regex(regex):
'''Compiling regular expressions is a very time consuming so return a
pre-compiled regex match object if possible.'''
@@ -43,7 +54,7 @@ class Keylet(object):
self.wasmod = False
def __repr__(self):
- return "<Keycmd(%r)>" % self.to_string()
+ return '<Keycmd(%r)>' % self.to_string()
def to_string(self):
@@ -75,7 +86,7 @@ def make_simple(key):
'''Make some obscure names for some keys friendlier.'''
# Remove left-right discrimination.
- if key.endswith("_L") or key.endswith("_R"):
+ if key.endswith('_L') or key.endswith('_R'):
key = key[:-2]
if key in _SIMPLEKEYS:
@@ -87,14 +98,14 @@ def make_simple(key):
def add_instance(uzbl, *args):
'''Create the Keylet object for this uzbl instance.'''
- _UZBLS[uzbl] = Keylet()
+ UZBLS[uzbl] = Keylet()
def del_instance(uzbl, *args):
'''Delete the Keylet object for this uzbl instance.'''
- if uzbl in _UZBLS:
- del _UZBLS[uzbl]
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
def get_keylet(uzbl):
@@ -102,10 +113,10 @@ def get_keylet(uzbl):
# 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:
+ if uzbl not in UZBLS:
add_instance(uzbl)
- keylet = _UZBLS[uzbl]
+ keylet = UZBLS[uzbl]
keylet._to_string = None
return keylet
@@ -124,22 +135,34 @@ def clear_keycmd(uzbl):
k.wasmod = True
k.modcmd = False
- uzbl.config['keycmd'] = ""
- uzbl.event("KEYCMD_CLEAR")
+ config = uzbl.get_config()
+ if 'keycmd' not in config or config['keycmd'] != '':
+ config['keycmd'] = ''
+
+ uzbl.event('KEYCMD_CLEAR')
def update_event(uzbl, keylet):
'''Raise keycmd/modcmd update events.'''
+ config = uzbl.get_config()
+
if keylet.modcmd:
- uzbl.config['keycmd'] = keylet.to_string()
- uzbl.event("MODCMD_UPDATE", keylet)
+ keycmd = keylet.to_string()
+ uzbl.event('MODCMD_UPDATE', keylet)
+ if keycmd != keylet.to_string():
+ return
- elif 'keycmd_events' not in uzbl.config or uzbl.config['keycmd_events'] == '1':
+ if 'modcmd_updates' in config and config['modcmd_updates'] != '1':
+ return
+
+ elif 'keycmd_events' not in config or config['keycmd_events'] == '1':
keycmd = keylet.cmd + keylet.cmd_s
uzbl.event('KEYCMD_UPDATE', keylet)
- if keycmd == (keylet.cmd + keylet.cmd_s):
- uzbl.config['keycmd'] = keylet.cmd + keylet.cmd_s
+ if keycmd != (keylet.cmd + keylet.cmd_s):
+ return
+
+ uzbl.set('keycmd', keycmd_escape(keycmd))
def key_press(uzbl, key):
@@ -153,13 +176,12 @@ def key_press(uzbl, key):
a. BackSpace deletes the last character in the keycmd.
b. Return raises a KEYCMD_EXEC event then clears the keycmd.
c. Escape clears the keycmd.
- d. Normal keys are added to held keys list (I.e. <a><b>+c).
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_"):
+ if key.startswith('Shift_'):
return
if len(key) > 1:
@@ -202,7 +224,9 @@ def key_press(uzbl, key):
cmdmod = True
elif not k.modcmd and key == 'Return':
- uzbl.event("KEYCMD_EXEC", k)
+ if k.cmd:
+ uzbl.event('KEYCMD_EXEC', k)
+
clear_keycmd(uzbl)
elif not k.modcmd and key == 'Escape':
@@ -221,18 +245,18 @@ def key_press(uzbl, key):
elif key == 'e':
k.cmd = k.cmd + k.cmd_s
k.cmd_s = ''
-
+
elif not k.held and not k.cmd and len(key) > 1:
k.modcmd = True
k.held.append(key)
- k.held.sort()
cmdmod = True
- if not k.modcmd:
- k.cmd += key
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()
@@ -241,7 +265,8 @@ def key_press(uzbl, key):
k.cmd += key
else:
- if 'keycmd_events' not in uzbl.config or uzbl.config['keycmd_events'] == '1':
+ config = uzbl.get_config()
+ if 'keycmd_events' not in config or config['keycmd_events'] == '1':
if len(key) == 1:
cmdmod = True
k.cmd += key
@@ -281,14 +306,9 @@ def key_release(uzbl, key):
if key in k.held:
if k.modcmd:
uzbl.event('MODCMD_EXEC', k)
- clear_keycmd(uzbl)
-
- k.held.remove(key)
- elif not k.modcmd and key in k.held:
k.held.remove(key)
- k.held.sort()
- cmdmod = True
+ clear_keycmd(uzbl)
if not k.held and not k.cmd and k.wasmod:
k.wasmod = False
@@ -296,17 +316,24 @@ def key_release(uzbl, key):
if cmdmod:
update_event(uzbl, k)
-def config_changed(uzbl, k, v):
- if k == 'keycmd':
- keylet = get_keylet(uzbl)
- if v != keylet.cmd + keylet.cmd_s:
- keylet.cmd,keylet.cmd_s = v,''
+
+def config_changed(uzbl, key, value):
+ '''Check for external keycmd updates and update the keylet accordingly.'''
+
+ if key == 'keycmd':
+ k = get_keylet(uzbl)
+ if value != k.cmd + k.cmd_s:
+ k.cmd = value
+ k.cmd_s = ''
+
def init(uzbl):
'''Connect handlers to uzbl events.'''
- uzbl.connect('INSTANCE_START', add_instance)
- uzbl.connect('INSTANCE_STOP', del_instance)
- uzbl.connect('KEY_PRESS', key_press)
- uzbl.connect('KEY_RELEASE', key_release)
- uzbl.connect('CONFIG_CHANGED', config_changed)
+ connects = {'INSTANCE_START': add_instance,
+ 'INSTANCE_EXIT': del_instance,
+ 'KEY_PRESS': key_press,
+ 'KEY_RELEASE': key_release,
+ 'CONFIG_CHANGED': config_changed}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/mode.py b/examples/data/uzbl/scripts/plugins/mode.py
new file mode 100644
index 0000000..ad0d9a8
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/mode.py
@@ -0,0 +1,159 @@
+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
new file mode 100644
index 0000000..242f9b0
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/on_event.py
@@ -0,0 +1,117 @@
+'''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 "<all args> <arg 0> <arg 1>...".'''
+
+ 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 <EVENT_NAME> 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
new file mode 100644
index 0000000..03cb748
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/plugin_template.py
@@ -0,0 +1,75 @@
+'''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
new file mode 100644
index 0000000..b6fcb1b
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/progress_bar.py
@@ -0,0 +1,158 @@
+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 <key> = <value>
+ '''
+
+ 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)