aboutsummaryrefslogtreecommitdiffhomepage
path: root/examples
diff options
context:
space:
mode:
authorGravatar Dieter Plaetinck <dieter@plaetinck.be>2009-12-06 18:07:47 +0100
committerGravatar Dieter Plaetinck <dieter@plaetinck.be>2009-12-06 18:07:47 +0100
commitc150136f5b772c35a633afadecf6e3e491ff4ae4 (patch)
treedaa591eab1545753ea7b9003451cb18fe7e7bdca /examples
parent436c319e2e2543a04c34e1f79b2ab10bc391c927 (diff)
parentf2341aee3860107450b453486336133dafbcdd8b (diff)
Merge branch 'master' into experimental
Diffstat (limited to 'examples')
-rw-r--r--examples/config/uzbl/config14
-rw-r--r--examples/data/uzbl/plugins/bind.py143
-rw-r--r--examples/data/uzbl/plugins/completion.py2
-rw-r--r--examples/data/uzbl/plugins/config.py41
-rw-r--r--examples/data/uzbl/plugins/keycmd.py68
-rw-r--r--examples/data/uzbl/plugins/mode.py17
-rwxr-xr-xexamples/data/uzbl/scripts/uzbl-event-manager340
7 files changed, 318 insertions, 307 deletions
diff --git a/examples/config/uzbl/config b/examples/config/uzbl/config
index f3e9f6a..5585195 100644
--- a/examples/config/uzbl/config
+++ b/examples/config/uzbl/config
@@ -10,7 +10,7 @@ set bind = request BIND
# request MODE_CONFIG <mode> <key> = <value
set mode_config = request MODE_CONFIG
# request TOGGLE_MODES <mode1> <mode2> ... <moden>
-set toggle_modes = request TOGGLE_MODES
+set toggle_modes = event TOGGLE_MODES
# request ON_EVENT <EVENT_NAME> <command>
set on_event = request ON_EVENT
# request PROGRESS_CONFIG <key> = <value>
@@ -29,6 +29,8 @@ set shell_cmd = sh -c
# Spawn path shortcuts. In spawn the first dir+path match is used in "dir1:dir2:dir3:executable"
set scripts_dir = $XDG_DATA_HOME/uzbl:@prefix/share/uzbl/examples/data/uzbl:scripts
+# Javascipt helpers.
+set jsh = js var run=Uzbl.run; function get(k){return run("print \\\@"+k)}; function set(k, v) {run("set "+k+" = "+v)};
# === Handlers ===============================================================
@@ -43,9 +45,11 @@ set new_window = sh 'uzbl-browser -u $8' # equivalent to the default beh
# Load start handlers
@on_event LOAD_START @set_status <span foreground="khaki">wait</span>
-# Load commit handler
+# Load commit handlers
@on_event LOAD_COMMIT @set_status <span foreground="green">recv</span>
@on_event LOAD_COMMIT script @scripts_dir/scroll-percentage.js
+# Reset the keycmd on navigation
+@on_event LOAD_COMMIT @set_mode
# Load finish handlers
@on_event LOAD_FINISH @set_status <span foreground="gold">done</span>
@@ -63,7 +67,6 @@ set new_window = sh 'uzbl-browser -u $8' # equivalent to the default beh
# Misc on_event handlers
#@on_event CONFIG_CHANGED print Config changed: %1 = %2
-
# === Behaviour and appearance ===============================================
set show_status = 1
@@ -137,10 +140,9 @@ set socket_dir = /tmp
# otherwise open the selection in the current window
set load_from_xclip = sh 'echo "uri $(xclip -o)" > $4'
set open_new_window = sh 'uzbl-browser -u \@SELECTED_URI'
-@bind <Button2> = js if("\@SELECTED_URI") { Uzbl.run("\@open_new_window"); } else { Uzbl.run("\\\@load_from_xclip"); }
+@bind <Button2> = @jsh if(get("SELECTED_URI")) { run("\@open_new_window"); } else { run("\\\@load_from_xclip"); }
# Edit HTML forms in external editor
-#
set external_editor = gvim
#set external_editor = xterm -e vim
@bind E = script @scripts_dir/extedit.js
@@ -312,7 +314,7 @@ set default_mode = command
# Changing mode method via set.
@bind I = @set_mode insert
-# Or toggle between modes by rasing request events.
+# Or toggle between modes by rasing the toggle event.
set toggle_cmd_ins = @toggle_modes command insert
@bind i = @toggle_cmd_ins
diff --git a/examples/data/uzbl/plugins/bind.py b/examples/data/uzbl/plugins/bind.py
index 3169b15..9702434 100644
--- a/examples/data/uzbl/plugins/bind.py
+++ b/examples/data/uzbl/plugins/bind.py
@@ -11,13 +11,15 @@ And it is also possible to execute a function on activation:
import sys
import re
+import pprint
# Export these functions to uzbl.<name>
__export__ = ['bind', 'del_bind', 'del_bind_by_glob', 'get_binds']
# Hold the bind dicts for each uzbl instance.
UZBLS = {}
-DEFAULTS = {'binds': [], 'depth': 0, 'stack': [], 'args': [], 'last_mode': ''}
+DEFAULTS = {'binds': [], 'depth': 0, 'stack': [], 'args': [],
+ 'last_mode': '', 'after': None}
# Commonly used regular expressions.
starts_with_mod = re.compile('^<([A-Z][A-Za-z0-9-_]*)>')
@@ -27,6 +29,10 @@ find_prompts = re.compile('<([^:>]*):(\"[^\"]*\"|\'[^\']*\'|[^>]*)>').split
ON_EXEC, HAS_ARGS, MOD_CMD, GLOB, MORE = range(5)
+class ArgumentError(Exception):
+ pass
+
+
def ismodbind(glob):
'''Return True if the glob specifies a modbind.'''
@@ -74,21 +80,11 @@ def get_binds(uzbl):
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):
+def get_filtered_binds(uzbl, bd):
'''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['stack'])
-
- return list(bind_dict['binds'])
+ return bd['stack'] if bd['depth'] else bd['binds']
def del_bind(uzbl, bind):
@@ -220,6 +216,20 @@ class Bind(object):
return self._repr_cache
+def expand(cmd, args):
+ '''Replaces "%s %1 %2 %3..." with "<all args> <arg 0> <arg 1>...".'''
+
+ if '%s' in cmd:
+ cmd = cmd.replace('%s', ' '.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 exec_bind(uzbl, bind, *args, **kargs):
'''Execute bind objects.'''
@@ -236,14 +246,7 @@ def exec_bind(uzbl, bind, *args, **kargs):
commands = []
for cmd in bind.commands:
- if '%s' in cmd:
- if len(args) > 1:
- for arg in args:
- cmd = cmd.replace('%s', arg, 1)
-
- elif len(args) == 1:
- cmd = cmd.replace('%s', args[0])
-
+ cmd = expand(cmd, args)
uzbl.send(cmd)
@@ -283,43 +286,51 @@ def mode_changed(uzbl, mode):
clear_stack(uzbl)
-def clear_stack(uzbl):
+def clear_stack(uzbl, bd=None):
'''Clear everything related to stacked binds.'''
- bind_dict = get_bind_dict(uzbl)
- bind_dict['stack'] = []
- bind_dict['depth'] = 0
- bind_dict['args'] = []
- if bind_dict['last_mode']:
- uzbl.set_mode(bind_dict['last_mode'])
- bind_dict['last_mode'] = ''
+ if bd is None:
+ bd = get_bind_dict(uzbl)
+
+ bd['stack'] = []
+ bd['depth'] = 0
+ bd['args'] = []
+ bd['after'] = None
+ if bd['last_mode']:
+ mode, bd['last_mode'] = bd['last_mode'], ''
+ uzbl.set_mode(mode)
- uzbl.set('keycmd_prompt', force=False)
+ uzbl.set('keycmd_prompt')
-def stack_bind(uzbl, bind, args, depth):
+def stack_bind(uzbl, bind, args, depth, bd):
'''Increment the stack depth in the bind dict, generate filtered bind
list for stack mode and set keycmd prompt.'''
- bind_dict = get_bind_dict(uzbl)
- if bind_dict['depth'] != depth:
- if bind not in bind_dict['stack']:
- bind_dict['stack'].append(bind)
+ if bd['depth'] != depth:
+ if bind not in bd['stack']:
+ bd['stack'].append(bind)
return
if uzbl.get_mode() != 'stack':
- bind_dict['last_mode'] = uzbl.get_mode()
+ bd['last_mode'] = uzbl.get_mode()
uzbl.set_mode('stack')
- globalcmds = [cmd for cmd in bind_dict['binds'] if cmd.is_global]
- bind_dict['stack'] = [bind,] + globalcmds
- bind_dict['args'] += args
- bind_dict['depth'] = depth + 1
+ globalcmds = [cmd for cmd in bd['binds'] if cmd.is_global]
+ bd['stack'] = [bind,] + globalcmds
+ bd['args'] += args
+ bd['depth'] = depth + 1
+ bd['after'] = bind.prompts[depth]
+
- uzbl.send('event BIND_STACK_LEVEL %d' % bind_dict['depth'])
+def after_bind(uzbl, bd):
+ '''Check if there are afte-actions to perform.'''
- (prompt, set) = bind.prompts[depth]
+ if bd['after'] is None:
+ return
+
+ (prompt, set), bd['after'] = bd['after'], None
if prompt:
uzbl.set('keycmd_prompt', '%s:' % prompt)
@@ -332,9 +343,11 @@ def stack_bind(uzbl, bind, args, depth):
else:
uzbl.clear_keycmd()
+ uzbl.send('event BIND_STACK_LEVEL %d' % bd['depth'])
+
+
+def match_and_exec(uzbl, bind, depth, keylet, bd):
-def match_and_exec(uzbl, bind, depth, keylet):
- bind_dict = get_bind_dict(uzbl)
(on_exec, has_args, mod_cmd, glob, more) = bind[depth]
held = keylet.held
@@ -363,62 +376,74 @@ def match_and_exec(uzbl, bind, depth, keylet):
return True
elif more:
- stack_bind(uzbl, bind, args, depth)
+ stack_bind(uzbl, bind, args, depth, bd)
return False
- args = bind_dict['args'] + args
+ args = bd['args'] + args
exec_bind(uzbl, bind, *args)
uzbl.set_mode()
if not has_args:
- clear_stack(uzbl)
+ clear_stack(uzbl, bd)
uzbl.clear_current()
return True
def keycmd_update(uzbl, keylet):
- depth = get_stack_depth(uzbl)
- for bind in get_filtered_binds(uzbl):
+ bd = get_bind_dict(uzbl)
+ depth = bd['depth']
+ for bind in get_filtered_binds(uzbl, bd):
t = bind[depth]
if t[MOD_CMD] or t[ON_EXEC]:
continue
- if match_and_exec(uzbl, bind, depth, keylet):
+ if match_and_exec(uzbl, bind, depth, keylet, bd):
return
+ after_bind(uzbl, bd)
+
def keycmd_exec(uzbl, keylet):
- depth = get_stack_depth(uzbl)
- for bind in get_filtered_binds(uzbl):
+ bd = get_bind_dict(uzbl)
+ depth = bd['depth']
+ for bind in get_filtered_binds(uzbl, bd):
t = bind[depth]
if t[MOD_CMD] or not t[ON_EXEC]:
continue
- if match_and_exec(uzbl, bind, depth, keylet):
+ if match_and_exec(uzbl, bind, depth, keylet, bd):
return uzbl.clear_keycmd()
+ after_bind(uzbl, bd)
+
def modcmd_update(uzbl, keylet):
- depth = get_stack_depth(uzbl)
- for bind in get_filtered_binds(uzbl):
+ bd = get_bind_dict(uzbl)
+ depth = bd['depth']
+ for bind in get_filtered_binds(uzbl, bd):
t = bind[depth]
if not t[MOD_CMD] or t[ON_EXEC]:
continue
- if match_and_exec(uzbl, bind, depth, keylet):
+ if match_and_exec(uzbl, bind, depth, keylet, bd):
return
+ after_bind(uzbl, bd)
+
def modcmd_exec(uzbl, keylet):
- depth = get_stack_depth(uzbl)
- for bind in get_filtered_binds(uzbl):
+ bd = get_bind_dict(uzbl)
+ depth = bd['depth']
+ for bind in get_filtered_binds(uzbl, bd):
t = bind[depth]
if not t[MOD_CMD] or not t[ON_EXEC]:
continue
- if match_and_exec(uzbl, bind, depth, keylet):
+ if match_and_exec(uzbl, bind, depth, keylet, bd):
return uzbl.clear_modcmd()
+ after_bind(uzbl, bd)
+
def init(uzbl):
connects = {'BIND': parse_bind_event,
diff --git a/examples/data/uzbl/plugins/completion.py b/examples/data/uzbl/plugins/completion.py
index 42e7e17..770f310 100644
--- a/examples/data/uzbl/plugins/completion.py
+++ b/examples/data/uzbl/plugins/completion.py
@@ -194,5 +194,5 @@ def init(uzbl):
# And connect the dicts event handlers to the handler stack.
uzbl.connect_dict(connects)
- for event in ['STOP_COMPLETION', 'KEYCMD_EXEC', 'KEYCMD_CLEAR']:
+ for event in ['STOP_COMPLETION', 'KEYCMD_EXEC', 'KEYCMD_CLEARED']:
uzbl.connect(event, stop_completion)
diff --git a/examples/data/uzbl/plugins/config.py b/examples/data/uzbl/plugins/config.py
index 47b59f9..b43161b 100644
--- a/examples/data/uzbl/plugins/config.py
+++ b/examples/data/uzbl/plugins/config.py
@@ -3,8 +3,8 @@ import types
__export__ = ['set', 'get_config']
-_VALIDSETKEY = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$").match
-_TYPECONVERT = {'int': int, 'float': float, 'str': unicode}
+VALIDKEY = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$").match
+TYPECONVERT = {'int': int, 'float': float, 'str': unicode}
UZBLS = {}
@@ -15,14 +15,7 @@ def escape(value):
return unicode(value)
-def get_config(uzbl):
- if uzbl not in UZBLS:
- add_instance(uzbl)
-
- return UZBLS[uzbl]
-
-
-def set(uzbl, key, value='', force=True):
+def set(uzbl, key, value='', config=None, force=False):
'''Sends a: "set key = value" command to the uzbl instance. If force is
False then only send a set command if the values aren't equal.'''
@@ -32,7 +25,7 @@ def set(uzbl, key, value='', force=True):
else:
value = unicode(value)
- if not _VALIDSETKEY(key):
+ if not VALIDKEY(key):
raise KeyError("%r" % key)
value = escape(value)
@@ -40,13 +33,25 @@ def set(uzbl, key, value='', force=True):
value = value.replace("\n", "\\n")
if not force:
- config = get_config(uzbl)
+ if config is None:
+ config = get_config(uzbl)
+
if key in config and config[key] == value:
return
uzbl.send('set %s = %s' % (key, value))
+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.'''
+
+ set(self._uzbl, key, value, config=self)
+
+
def add_instance(uzbl, *args):
UZBLS[uzbl] = ConfigDict(uzbl)
@@ -63,22 +68,12 @@ def get_config(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.'''
-
- set(self._uzbl, key, value, force=False)
-
-
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)
+ value = TYPECONVERT[type](value)
dict.__setitem__(config, key, value)
diff --git a/examples/data/uzbl/plugins/keycmd.py b/examples/data/uzbl/plugins/keycmd.py
index 4c88fd8..af6beff 100644
--- a/examples/data/uzbl/plugins/keycmd.py
+++ b/examples/data/uzbl/plugins/keycmd.py
@@ -33,6 +33,7 @@ class Keylet(object):
def __init__(self):
# Modcmd tracking
self.held = set()
+ self.ignored = set()
self.modcmd = ''
self.is_modcmd = False
@@ -82,15 +83,18 @@ class Keylet(object):
results in a modkey addition. Return that addition and remove all
modkeys that created it.'''
- already_added = self.held & set(self.additions.keys())
- for key in already_added:
- if modkey in self.additions[key]:
+ # Intersection of (held list + modkey) and additions.
+ added = (self.held | set([modkey])) & set(self.additions.keys())
+ for key in added:
+ if key == modkey or modkey in self.additions[key]:
+ self.held -= self.additions[key]
return key
- modkeys = set(list(self.held) + [modkey,])
+ # Held list + ignored list + modkey.
+ modkeys = self.held | self.ignored | set([modkey])
for (key, value) in self.additions.items():
if modkeys.issuperset(value):
- self.held = modkeys ^ value
+ self.held -= value
return key
return modkey
@@ -256,11 +260,8 @@ def clear_keycmd(uzbl):
k.keycmd = ''
k.cursor = 0
k._repr_cache = False
- config = uzbl.get_config()
- if 'keycmd' not in config or config['keycmd']:
- uzbl.set('keycmd')
-
- uzbl.event('KEYCMD_CLEAR')
+ uzbl.set('keycmd')
+ uzbl.event('KEYCMD_CLEARED')
def clear_modcmd(uzbl, clear_held=False):
@@ -271,13 +272,11 @@ def clear_modcmd(uzbl, clear_held=False):
k.is_modcmd = False
k._repr_cache = False
if clear_held:
+ k.ignored = set()
k.held = set()
- config = uzbl.get_config()
- if 'modcmd' not in config or config['modcmd']:
- uzbl.set('modcmd')
-
- uzbl.event('MODCMD_CLEAR')
+ uzbl.set('modcmd')
+ uzbl.event('MODCMD_CLEARED')
def clear_current(uzbl):
@@ -314,22 +313,25 @@ def update_event(uzbl, k, execute=True):
if 'modcmd_updates' not in config or config['modcmd_updates'] == '1':
new_modcmd = k.get_modcmd()
if not new_modcmd:
- uzbl.set('modcmd')
+ uzbl.set('modcmd', config=config)
elif new_modcmd == modcmd:
- uzbl.set('modcmd', "<span> %s </span>" % uzbl_escape(new_modcmd))
+ uzbl.set('modcmd', '<span> %s </span>' % uzbl_escape(new_modcmd),
+ config=config)
if 'keycmd_events' in config and config['keycmd_events'] != '1':
return
- keycmd = k.get_keycmd()
- if not keycmd:
- return uzbl.set('keycmd')
+ new_keycmd = k.get_keycmd()
+ if not new_keycmd:
+ uzbl.set('keycmd', config=config)
- # Generate the pango markup for the cursor in the keycmd.
- curchar = keycmd[k.cursor] if k.cursor < len(keycmd) else ' '
- chunks = [keycmd[:k.cursor], curchar, keycmd[k.cursor+1:]]
- uzbl.set('keycmd', KEYCMD_FORMAT % tuple(map(uzbl_escape, chunks)))
+ elif new_keycmd == keycmd:
+ # Generate the pango markup for the cursor in the keycmd.
+ curchar = keycmd[k.cursor] if k.cursor < len(keycmd) else ' '
+ chunks = [keycmd[:k.cursor], curchar, keycmd[k.cursor+1:]]
+ value = KEYCMD_FORMAT % tuple(map(uzbl_escape, chunks))
+ uzbl.set('keycmd', value, config=config)
def inject_str(str, index, inj):
@@ -338,7 +340,7 @@ def inject_str(str, index, inj):
return "%s%s%s" % (str[:index], inj, str[index:])
-def get_keylet_and_key(uzbl, key):
+def get_keylet_and_key(uzbl, key, add=True):
'''Return the keylet and apply any transformations to the key as defined
by the modmapping or modkey addition rules. Return None if the key is
ignored.'''
@@ -349,6 +351,14 @@ def get_keylet_and_key(uzbl, key):
return (keylet, key)
modkey = "<%s>" % key.strip("<>")
+
+ if keylet.key_ignored(modkey):
+ if add:
+ keylet.ignored.add(modkey)
+
+ elif modkey in keylet.ignored:
+ keylet.ignored.remove(modkey)
+
modkey = keylet.find_addition(modkey)
if keylet.key_ignored(modkey):
@@ -380,9 +390,7 @@ def key_press(uzbl, key):
if 'keycmd_events' in config and config['keycmd_events'] != '1':
k.keycmd = ''
k.cursor = 0
- if config['keycmd']:
- uzbl.set('keycmd')
-
+ uzbl.set('keycmd', config=config)
return
k.keycmd = inject_str(k.keycmd, k.cursor, key)
@@ -408,9 +416,7 @@ def key_release(uzbl, key):
3. Check if any modkey is held, if so set modcmd mode.
4. Update the keycmd uzbl variable if anything changed.'''
- (k, key) = get_keylet_and_key(uzbl, key.strip())
- if not key:
- return
+ (k, key) = get_keylet_and_key(uzbl, key.strip(), add=False)
if key in k.held:
if k.is_modcmd:
diff --git a/examples/data/uzbl/plugins/mode.py b/examples/data/uzbl/plugins/mode.py
index 52b104a..f85d999 100644
--- a/examples/data/uzbl/plugins/mode.py
+++ b/examples/data/uzbl/plugins/mode.py
@@ -58,16 +58,13 @@ def get_mode(uzbl):
def mode_changed(uzbl, mode):
'''The mode has just been changed, now set the per-mode config.'''
- get_mode_dict(uzbl)['mode'] = mode
+ if get_mode(uzbl) != mode:
+ return
config = uzbl.get_config()
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
+ uzbl.set(key, value, config=config)
if 'mode_indicator' not in mode_config:
config['mode_indicator'] = mode
@@ -96,12 +93,10 @@ def set_mode(uzbl, mode=None):
if 'mode' not in config or config['mode'] != mode:
config['mode'] = mode
- return
-
- elif get_mode(uzbl) == mode:
- return
- uzbl.event("MODE_CHANGED", mode)
+ elif mode_dict['mode'] != mode:
+ mode_dict['mode'] = mode
+ uzbl.event("MODE_CHANGED", mode)
def config_changed(uzbl, key, value):
diff --git a/examples/data/uzbl/scripts/uzbl-event-manager b/examples/data/uzbl/scripts/uzbl-event-manager
index 0054ff6..6669282 100755
--- a/examples/data/uzbl/scripts/uzbl-event-manager
+++ b/examples/data/uzbl/scripts/uzbl-event-manager
@@ -30,7 +30,6 @@ import imp
import os
import sys
import re
-import types
import socket
import pprint
import time
@@ -39,6 +38,7 @@ from select import select
from signal import signal, SIGTERM
from optparse import OptionParser
from traceback import print_exc
+from functools import partial
# ============================================================================
@@ -63,8 +63,9 @@ 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 = {
+# Event manager config dictionary. This is not to be confused with the config
+# dict that tracks variables in the uzbl instance.
+CONFIG = {
'verbose': False,
'daemon_mode': True,
'auto_close': False,
@@ -86,20 +87,25 @@ config = {
# Define some globals.
-_SCRIPTNAME = os.path.basename(sys.argv[0])
-_RE_FINDSPACES = re.compile("\s+")
+SCRIPTNAME = os.path.basename(sys.argv[0])
+FINDSPACES = re.compile("\s+")
+
+
+class ArgumentError(Exception):
+ pass
+
def echo(msg):
'''Prints only if the verbose flag has been set.'''
- if config['verbose']:
- sys.stdout.write("%s: %s\n" % (_SCRIPTNAME, msg))
+ if CONFIG['verbose']:
+ sys.stdout.write("%s: %s\n" % (SCRIPTNAME, msg))
def error(msg):
'''Prints error messages to stderr.'''
- sys.stderr.write("%s: error: %s\n" % (_SCRIPTNAME, msg))
+ sys.stderr.write("%s: error: %s\n" % (SCRIPTNAME, msg))
def counter():
@@ -111,18 +117,6 @@ def counter():
yield i
-def iscallable(obj):
- '''Return true if the object is callable.'''
-
- return hasattr(obj, "__call__")
-
-
-def isiterable(obj):
- '''Return true if you can iterate over the item.'''
-
- return hasattr(obj, "__iter__")
-
-
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', ...}'''
@@ -134,23 +128,26 @@ def find_plugins(plugin_dirs):
if not os.path.isdir(plugin_dir):
continue
- for file in os.listdir(plugin_dir):
- if not file.lower().endswith('.py'):
+ for filename in os.listdir(plugin_dir):
+ if not filename.lower().endswith('.py'):
continue
- path = os.path.join(plugin_dir, file)
+ path = os.path.join(plugin_dir, filename)
if not os.path.isfile(path):
continue
- if file not in plugins:
- plugins[file] = plugin_dir
+ if filename not in plugins:
+ plugins[filename] = plugin_dir
return plugins
-def load_plugins(plugin_dirs, load=[], ignore=[]):
+def load_plugins(plugin_dirs, load=None, ignore=None):
'''Load event manager plugins found in the plugin_dirs.'''
+ load = [] if load is None else load
+ ignore = [] if ignore is None else ignore
+
# Find the plugins in the plugin_dirs.
found = find_plugins(plugin_dirs)
@@ -171,11 +168,11 @@ def load_plugins(plugin_dirs, load=[], ignore=[]):
loaded = {}
# Load all found plugins into the loaded dict.
- for (filename, dir) in found.items():
+ for (filename, plugin_dir) in found.items():
name = filename[:-3]
- info = imp.find_module(name, [dir,])
+ info = imp.find_module(name, [plugin_dir])
plugin = imp.load_module(name, *info)
- loaded[(dir, filename)] = plugin
+ loaded[(plugin_dir, filename)] = plugin
return loaded
@@ -230,9 +227,9 @@ def make_pid_file(pid_file):
'''Make pid file at given pid_file location.'''
make_dirs(pid_file)
- file = open(pid_file, 'w')
- file.write('%d' % os.getpid())
- file.close()
+ fileobj = open(pid_file, 'w')
+ fileobj.write('%d' % os.getpid())
+ fileobj.close()
def del_pid_file(pid_file):
@@ -246,13 +243,12 @@ 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())
+ fileobj = open(pid_file, 'r')
+ pid = int(fileobj.read())
+ fileobj.close()
return pid
- except:
+ except IOError, ValueError:
print_exc()
return None
@@ -289,16 +285,32 @@ def term_process(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.'''
+def parse_msg(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.'''
- locals = (function, pre_args)
- def _prepender(*args, **kargs):
- (function, pre_args) = locals
- return function(*(pre_args + args), **kargs)
+ if not msg:
+ return
- return _prepender
+ cmd = FINDSPACES.split(msg, 3)
+ if not cmd or cmd[0] != 'EVENT':
+ # Not an event message.
+ print '---', msg
+ return
+
+ while len(cmd) < 4:
+ cmd.append('')
+
+ event, args = cmd[2], cmd[3]
+ if not event:
+ return
+
+ try:
+ uzbl.event(event, args)
+
+ except:
+ print_exc()
class EventHandler(object):
@@ -306,7 +318,7 @@ class EventHandler(object):
nexthid = counter().next
def __init__(self, event, handler, *args, **kargs):
- if not iscallable(handler):
+ if not callable(handler):
raise ArgumentError("EventHandler object requires a callable "
"object function for the handler argument not: %r" % handler)
@@ -333,7 +345,7 @@ class EventHandler(object):
class UzblInstance(object):
# Give all plugins access to the main config dict.
- config = config
+ config = CONFIG
def __init__(self, parent, client_socket):
@@ -378,8 +390,8 @@ class UzblInstance(object):
raise KeyError("conflicting export: %r" % export)
obj = getattr(plugin, export)
- if iscallable(obj):
- obj = prepender(obj, self)
+ if callable(obj):
+ obj = partial(obj, self)
self._exports[export] = obj
@@ -442,7 +454,7 @@ class UzblInstance(object):
handlers.remove(handler)
return
- echo('unable to find & remove handler with id: %d' % handler.hid)
+ echo('unable to find & remove handler with id: %d' % hid)
def remove(self, handler):
@@ -516,7 +528,7 @@ class UzblEventDaemon(dict):
# Register that the event daemon server has started by creating the
# pid file.
- make_pid_file(config['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)
@@ -525,15 +537,15 @@ class UzblEventDaemon(dict):
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'])
+ 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 = CONFIG['server_socket']
server_socket = os.path.realpath(os.path.expandvars(server_socket))
self.socket_location = server_socket
@@ -563,11 +575,11 @@ class UzblEventDaemon(dict):
def run(self):
'''Main event daemon loop.'''
- if config['daemon_mode']:
+ if CONFIG['daemon_mode']:
echo('entering daemon mode.')
daemonize()
# The pid has changed so update the pid file.
- make_pid_file(config['pid_file'])
+ make_pid_file(CONFIG['pid_file'])
# Create event daemon socket.
self._create_server_socket()
@@ -587,18 +599,18 @@ class UzblEventDaemon(dict):
self.running = True
while self.running:
- sockets = [self.server_socket,] + self['uzbls'].keys()
+ sockets = [self.server_socket] + self['uzbls'].keys()
- read, _, error = select(sockets, [], sockets, 1)
+ reads, _, errors = select(sockets, [], sockets, 1)
- if self.server_socket in read:
+ if self.server_socket in reads:
self.accept_connection()
- read.remove(self.server_socket)
+ reads.remove(self.server_socket)
- for client in read:
+ for client in reads:
self.read_socket(client)
- for client in error:
+ for client in errors:
error('Unknown error on socket: %r' % client)
self.close_connection(client)
@@ -607,55 +619,28 @@ class UzblEventDaemon(dict):
'''Read data from an instance socket and pass to the uzbl objects
event handler function.'''
+ uzbl = self['uzbls'][client]
try:
- uzbl = self['uzbls'][client]
- try:
- raw = unicode(client.recv(8192), 'utf-8', 'ignore')
-
- except:
- print_exc()
- raw = None
-
- if not raw:
- # Read null byte, close socket.
- return self.close_connection(client)
-
- uzbl.buffer += raw
- msgs = uzbl.buffer.split('\n')
- uzbl.buffer = msgs.pop()
-
- for msg in msgs:
- self.parse_msg(uzbl, msg)
+ raw = unicode(client.recv(8192), 'utf-8', 'ignore')
except:
- raise
-
-
- 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.'''
-
- msg = msg.strip()
- if not msg:
- return
-
- cmd = _RE_FINDSPACES.split(msg, 3)
- if not cmd or cmd[0] != 'EVENT':
- # Not an event message.
- print '---', msg
- return
+ print_exc()
+ raw = None
- if len(cmd) < 4:
- cmd.append('')
+ if not raw:
+ # Read null byte, close socket.
+ return self.close_connection(client)
- event, args = cmd[2], cmd[3]
+ uzbl.buffer += raw
+ msgs = uzbl.buffer.split('\n')
+ uzbl.buffer = msgs.pop()
- try:
- uzbl.event(event, args)
+ for msg in msgs:
+ try:
+ parse_msg(uzbl, msg.strip())
- except:
- print_exc()
+ except:
+ print_exc()
def accept_connection(self):
@@ -679,7 +664,7 @@ class UzblEventDaemon(dict):
except:
print_exc()
- if not len(self['uzbls']) and config['auto_close']:
+ if not len(self['uzbls']) and CONFIG['auto_close']:
echo('auto closing event manager.')
self.running = False
@@ -696,14 +681,14 @@ class UzblEventDaemon(dict):
echo('unlinking: %r' % self.socket_location)
self._close_server_socket()
- echo('deleting pid file: %r' % config['pid_file'])
- del_pid_file(config['pid_file'])
+ echo('deleting pid file: %r' % CONFIG['pid_file'])
+ del_pid_file(CONFIG['pid_file'])
-def stop():
+def stop_action():
'''Stop the event manager daemon.'''
- pid_file = config['pid_file']
+ pid_file = CONFIG['pid_file']
if not os.path.isfile(pid_file):
return echo('no running daemon found.')
@@ -721,10 +706,10 @@ def stop():
echo('stopped event daemon.')
-def start():
+def start_action():
'''Start the event manager daemon.'''
- pid_file = config['pid_file']
+ pid_file = CONFIG['pid_file']
if os.path.isfile(pid_file):
echo('found pid file: %r' % pid_file)
pid = get_pid(pid_file)
@@ -738,126 +723,129 @@ def start():
UzblEventDaemon().run()
-def restart():
+def restart_action():
'''Restart the event manager daemon.'''
echo('restarting event manager daemon.')
- stop()
- start()
+ stop_action()
+ start_action()
-def list_plugins():
+def list_action():
'''List all the plugins being loaded by the event daemon.'''
- plugins = find_plugins(config['plugin_dirs'])
+ plugins = find_plugins(CONFIG['plugin_dirs'])
dirs = {}
- for (plugin, dir) in plugins.items():
- if dir not in dirs:
- dirs[dir] = []
+ for (plugin, plugin_dir) in plugins.items():
+ if plugin_dir not in dirs:
+ dirs[plugin_dir] = []
- dirs[dir].append(plugin)
+ dirs[plugin_dir].append(plugin)
- for (index, (dir, plugin_list)) in enumerate(sorted(dirs.items())):
+ for (index, (plugin_dir, plugin_list)) in enumerate(sorted(dirs.items())):
if index:
print
- print "%s:" % dir
+ print "%s:" % plugin_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",
+ 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-dirs', dest='plugin_dirs', action="store",
+ 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('-l', '--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",
+ PARSER.add_option('-i', '--ignore-plugins', dest="ignore", action="store",
metavar="PLUGINS", help="comma separated list of plugins to ignore")
- parser.add_option('-p', '--pid-file', dest='pid', action='store',
+ 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',
+ 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",
+ PARSER.add_option('-n', '--no-daemon', dest="daemon",
action="store_true", help="don't enter daemon mode.")
- parser.add_option('-a', '--auto-close', dest='autoclose',
+ PARSER.add_option('-a', '--auto-close', dest='autoclose',
action='store_true', help='auto close after all instances disconnect.')
- (options, args) = parser.parse_args()
+ (OPTIONS, ARGS) = PARSER.parse_args()
- # init like {start|stop|..} daemon control section.
- daemon_controls = {'start': start, 'stop': stop, 'restart': restart,
- 'list': list_plugins}
+ # init like {start|stop|..} daemon actions dict.
+ DAEMON_ACTIONS = {'start': start_action, 'stop': stop_action,
+ 'restart': restart_action, 'list': list_action}
- if len(args) == 1:
- action = args[0]
- if action not in daemon_controls:
- error('unknown action: %r' % action)
- sys.exit(1)
+ if not ARGS:
+ ACTION = 'start'
- elif len(args) > 1:
- error("too many arguments: %r" % args)
- sys.exit(1)
+ elif len(ARGS) == 1:
+ ACTION = ARGS[0]
+ if ACTION not in DAEMON_ACTIONS:
+ raise ArgumentError("unknown argument: %r" % ACTION)
else:
- action = 'start'
+ raise ArgumentError("too many arguments: %r" % ARGS)
# parse other flags & options.
- if options.verbose:
- config['verbose'] = True
+ if OPTIONS.verbose:
+ CONFIG['verbose'] = True
+
+ if OPTIONS.plugin_dirs:
+ PLUGIN_DIRS = []
+ for DIR in OPTIONS.plugin_dirs.split(':'):
+ if not DIR:
+ continue
+
+ PLUGIN_DIRS.append(os.path.realpath(DIR))
- if options.plugin_dirs:
- plugin_dirs = map(os.path.realpath, map(str.strip,
- options.plugin_dirs.split(':')))
- config['plugin_dirs'] = plugin_dirs
- echo("plugin search dirs: %r" % plugin_dirs)
+ CONFIG['plugin_dirs'] = PLUGIN_DIRS
+ echo("plugin search dirs: %r" % PLUGIN_DIRS)
- if options.load and options.ignore:
+ if OPTIONS.load and OPTIONS.ignore:
error("you can't load and ignore at the same time.")
sys.exit(1)
- elif options.load:
- plugins_load = config['plugins_load']
- for plugin in options.load.split(','):
- if plugin.strip():
- plugins_load.append(plugin.strip())
+ elif OPTIONS.load:
+ LOAD = CONFIG['plugins_load']
+ for PLUGIN in OPTIONS.load.split(','):
+ if PLUGIN.strip():
+ LOAD.append(PLUGIN.strip())
- echo('only loading plugin(s): %s' % ', '.join(plugins_load))
+ echo('only loading plugin(s): %s' % ', '.join(LOAD))
- elif options.ignore:
- plugins_ignore = config['plugins_ignore']
- for plugin in options.ignore.split(','):
- if plugin.strip():
- plugins_ignore.append(plugin.strip())
+ elif OPTIONS.ignore:
+ IGNORE = CONFIG['plugins_ignore']
+ for PLUGIN in OPTIONS.ignore.split(','):
+ if PLUGIN.strip():
+ IGNORE.append(PLUGIN.strip())
- echo('ignoring plugin(s): %s' % ', '.join(plugins_ignore))
+ echo('ignoring plugin(s): %s' % ', '.join(IGNORE))
- if options.autoclose:
- config['auto_close'] = True
+ if OPTIONS.autoclose:
+ CONFIG['auto_close'] = True
echo('will auto close.')
- if options.pid:
- config['pid_file'] = os.path.realpath(options.pid)
- echo("pid file location: %r" % config['pid_file'])
+ if OPTIONS.pid:
+ CONFIG['pid_file'] = os.path.realpath(OPTIONS.pid)
+ echo("pid file location: %r" % CONFIG['pid_file'])
- if options.socket:
- config['server_socket'] = os.path.realpath(options.socket)
- echo("daemon socket location: %s" % config['server_socket'])
+ if OPTIONS.socket:
+ CONFIG['server_socket'] = os.path.realpath(OPTIONS.socket)
+ echo("daemon socket location: %s" % CONFIG['server_socket'])
- if options.daemon:
- config['daemon_mode'] = False
+ if OPTIONS.daemon:
+ CONFIG['daemon_mode'] = False
# Now {start|stop|...}
- daemon_controls[action]()
+ DAEMON_ACTIONS[ACTION]()