diff options
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | examples/config/uzbl/config | 44 | ||||
-rw-r--r-- | examples/data/uzbl/plugins/bind.py | 97 | ||||
-rw-r--r-- | examples/data/uzbl/plugins/keycmd.py | 395 | ||||
-rwxr-xr-x | examples/data/uzbl/scripts/event_manager.py | 8 | ||||
-rwxr-xr-x | uzbl-browser | 47 | ||||
-rw-r--r-- | uzbl-core.c | 12 | ||||
-rwxr-xr-x | uzbl-daemon | 20 |
8 files changed, 368 insertions, 279 deletions
@@ -61,10 +61,10 @@ test-dev: uzbl-core test-dev-browser: uzbl-browser XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./examples/data/uzbl/scripts/cookie_daemon.py start -nv & - XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./uzbl-daemon start -nv & - XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./uzbl-browser --uri http://www.uzbl.org --verbose + XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./examples/data/uzbl/scripts/event_manager.py start -nv & #still needed? uzbl-browser takes care of this? + XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH:`pwd`/examples/data/uzbl/scripts/" ./uzbl-browser --uri http://www.uzbl.org --verbose XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./examples/data/uzbl/scripts/cookie_daemon.py stop -v - XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./uzbl-daemon stop -v + XDG_DATA_HOME=./examples/data XDG_CACHE_HOME=./examples/cache XDG_CONFIG_HOME=./examples/config PATH="`pwd`:$$PATH" ./examples/data/uzbl/scripts/event_manager.py stop -v test-share: uzbl-core XDG_DATA_HOME=${PREFIX}/share/uzbl/examples/data XDG_CONFIG_HOME=${PREFIX}/share/uzbl/examples/config ./uzbl-core --uri http://www.uzbl.org --verbose @@ -80,7 +80,9 @@ clean: rm -f inspector.o cd ./tests/; $(MAKE) clean -install: all +install: install-uzbl-core install-uzbl-browser install-uzbl-tabbed + +install-uzbl-core: all install -d $(PREFIX)/bin install -d $(PREFIX)/share/uzbl/docs install -d $(PREFIX)/share/uzbl/examples @@ -88,12 +90,22 @@ install: all cp -rp config.h $(PREFIX)/share/uzbl/docs/ cp -rp examples $(PREFIX)/share/uzbl/ install -m755 uzbl-core $(PREFIX)/bin/uzbl-core - install -m755 uzbl-browser $(PREFIX)/bin/uzbl-browser - install -m755 uzbl-daemon $(PREFIX)/bin/uzbl-daemon install -m644 AUTHORS $(PREFIX)/share/uzbl/docs install -m644 README $(PREFIX)/share/uzbl/docs +install-uzbl-browser: all + install -d $(PREFIX)/bin + install -m755 uzbl-browser $(PREFIX)/bin/uzbl-browser + install -m755 examples/data/uzbl/scripts/cookie_daemon.py $(PREFIX)/bin/cookie_daemon.py + install -m755 examples/data/uzbl/scripts/event_manager.py $(PREFIX)/bin/event_manager.py + sed -i 's#^PREFIX=.*#PREFIX=$(PREFIX)#' $(PREFIX)/bin/uzbl-browser + +install-uzbl-tabbed: all + install -d $(PREFIX)/bin + install -m755 examples/data/uzbl/scripts/uzbl_tabbed.py $(PREFIX)/bin/uzbl-tabbed uninstall: rm -rf $(PREFIX)/bin/uzbl-* + rm -rf $(PREFIX)/bin/cookie_daemon.py + rm -rf $(PREFIX)/bin/event_manager.py rm -rf $(PREFIX)/share/uzbl diff --git a/examples/config/uzbl/config b/examples/config/uzbl/config index 0ee9318..52de5e5 100644 --- a/examples/config/uzbl/config +++ b/examples/config/uzbl/config @@ -61,12 +61,13 @@ set show_status = 1 set status_top = 0 set status_background = #303030 -set keycmd_style = weight="bold" foreground="red" +set modcmd_style = weight="bold" foreground="red" +set keycmd_style = weight="light" foreground="red" set prompt_style = foreground="grey" set cursor_style = underline="single" set mode_section = <span background="khaki" foreground="black">[\@[\@mode_indicator]\@]</span> -set keycmd_section = [<span \@prompt_style>\@[\@keycmd_prompt]\@</span><span \@keycmd_style>\@keycmd</span>] +set keycmd_section = [<span \@prompt_style>\@[\@keycmd_prompt]\@</span><span \@modcmd_style>\@modcmd</span><span \@keycmd_style>\@keycmd</span>] set progress_section = <span foreground="#606060">\@[\@progress_format]\@</span> set uri_section = <span foreground="#99FF66">\@[\@uri]\@</span> set name_section = <span foreground="khaki">\@[\@NAME]\@</span> @@ -132,7 +133,8 @@ set socket_dir = /tmp @bind gg _ = uri http://www.google.com/search?q=%s # Enclose the executable in quotes if it has spaces. Any additional parameters you use will # appear AFTER the default parameters -#@bind B = spawn @scripts_dir/insert_bookmark.sh +# use a script to insert bookmarks. or use the EM/keycmd technique a bit further down +@bind B = spawn @scripts_dir/insert_bookmark.sh @bind U = spawn @scripts_dir/load_url_from_history.sh @bind u = spawn @scripts_dir/load_url_from_bookmarks.sh # with the sample yank script, you can yank one of the arguments into clipboard/selection @@ -158,7 +160,7 @@ set socket_dir = /tmp @bind XS = sh 'echo "js alert (\\"This is sent by the shell via a fifo\\")" > "$4"' @bind !dump = sh "echo dump_config > $4" -@bind !reload = sh 'cat $1 > $4' +@bind !reload = sh "sed '/^# === Post-load misc commands/,$d' $1 > $4" # this script allows you to configure (per domain) values to fill in form fields (eg login information) and to fill in these values automatically set formfiller = spawn @scripts_dir/formfiller @@ -186,13 +188,33 @@ set formfiller = spawn @scripts_dir/formfiller # Examples using multi-stage-bindings with text prompts. @bind o<uri:>_ = uri %s -# Prints tab separated "uri title keyword tags" to the bookmarks file. -# TODO: Improve bookmarks script to handle this format & include date in bookmark format. -@bind <Ctrl>b<name:>_<tags:>_ = sh 'echo -e "$6 $7 %s %s" >> $XDG_DATA_HOME/uzbl/bookmarks' +# multi-stage binding way to write bookmarks to file from inside uzbl. +@bind <Ctrl>b<tags:>_ = sh 'echo -e "$6 %s" >> $XDG_DATA_HOME/uzbl/bookmarks' # Multi-stage bindings with blank prompts (similar behaviour to emacs M-c M-s bindings?) -@bind <Ctrl>a<:>q = exit -@bind <Ctrl>a<:>h = uri http://uzbl.org/ +@bind <Ctrl>a<:><Ctrl>q = exit +@bind <Ctrl>a<:><Ctrl>h = uri http://uzbl.org/ + + +# === command editing configuration ========================================== + +# you'll want this at the very least +@bind <Return> = event KEYCMD_EXEC_CURRENT +@bind <Escape> = event @set_mode + +# basic searching +@bind <Home> = event SET_CURSOR_POS 0 +@bind <End> = event SET_CURSOR_POS -1 +@bind <Left> = event SET_CURSOR_POS - +@bind <Right> = event SET_CURSOR_POS + +@bind <BackSpace> = event KEYCMD_BACKSPACE +@bind <Delete> = event KEYCMD_DELETE + +# readline-ish bindings +@bind <Ctrl>w = event KEYCMD_STRIP_WORD +@bind <Ctrl>u = event SET_KEYCMD +@bind <Ctrl>a = event SET_CURSOR_POS 0 +@bind <Ctrl>e = event SET_CURSOR_POS -1 # === Context menu items ===================================================== @@ -215,7 +237,7 @@ set insert = @mode_config insert set stack = @mode_config stack # Command mode config. -@command keycmd_style = weight="bold" foreground="red" +@command keycmd_style = foreground="red" @command status_background = #202020 @command mode_indicator = Cmd @@ -241,7 +263,7 @@ set default_mode = command set toggle_cmd_ins = @toggle_modes command insert @bind i = @toggle_cmd_ins -@bind <Ctrl>i = @toggle_cmd_ins +@bind <Ctrl><i> = @toggle_cmd_ins # === Post-load misc commands =============================================== diff --git a/examples/data/uzbl/plugins/bind.py b/examples/data/uzbl/plugins/bind.py index d62872f..fe017eb 100644 --- a/examples/data/uzbl/plugins/bind.py +++ b/examples/data/uzbl/plugins/bind.py @@ -20,7 +20,7 @@ __export__ = ['bind', 'del_bind', 'del_bind_by_glob', 'get_binds'] UZBLS = {} # Commonly used regular expressions. -starts_with_mod = re.compile('^<([A-Z][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. @@ -135,12 +135,13 @@ class Bind(object): nextbid = counter().next def __init__(self, glob, handler, *args, **kargs): - self.callable = iscallable(handler) + self.is_callable = iscallable(handler) + self._repr_cache = None if not glob: raise ArgumentError('glob cannot be blank') - if self.callable: + if self.is_callable: self.function = handler self.args = args self.kargs = kargs @@ -172,9 +173,8 @@ class Bind(object): msg = 'found null segment after first prompt: %r' % split raise BindParseError(msg) - self.stack = [] - - for glob in split[::2]: + stack = [] + for (index, glob) in enumerate(reversed(split[::2])): # Is the binding a MODCMD or KEYCMD: mod_cmd = ismodbind(glob) @@ -185,13 +185,28 @@ class Bind(object): 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)) + stack.append((mod_cmd, on_exec, has_args, glob, index)) + + self.stack = list(reversed(stack)) + self.is_global = len(self.stack) == 1 + + + def __getitem__(self, depth): + '''Get bind info at a depth.''' + + if self.is_global: + return self.stack[0] + + return self.stack[depth] def __repr__(self): + if self._repr_cache: + return self._repr_cache + args = ['glob=%r' % self.glob, 'bid=%d' % self.bid] - if self.callable: + if self.is_callable: args.append('function=%r' % self.function) if self.args: args.append('args=%r' % self.args) @@ -204,13 +219,16 @@ class Bind(object): cmds = self.commands[0] if cmdlen == 1 else self.commands args.append('command%s=%r' % ('s' if cmdlen-1 else '', cmds)) - return '<Bind(%s)>' % ', '.join(args) + self._repr_cache = '<Bind(%s)>' % ', '.join(args) + return self._repr_cache def exec_bind(uzbl, bind, *args, **kargs): '''Execute bind objects.''' - if bind.callable: + uzbl.event("EXEC_BIND", bind, args, kargs) + + if bind.is_callable: args += bind.args kargs = dict(bind.kargs.items()+kargs.items()) bind.function(uzbl, *args, **kargs) @@ -220,7 +238,6 @@ def exec_bind(uzbl, bind, *args, **kargs): raise ArgumentError('cannot supply kargs for uzbl commands') commands = [] - for cmd in bind.commands: if '%s' in cmd: if len(args) > 1: @@ -300,93 +317,101 @@ def filter_bind(uzbl, bind_dict, bind): def match_and_exec(uzbl, bind, depth, keycmd): bind_dict = get_bind_dict(uzbl) - mode_cmd, on_exec, has_args, glob = bind.stack[depth] + (mod_cmd, on_exec, has_args, glob, more) = bind[depth] if has_args: if not keycmd.startswith(glob): - filter_bind(uzbl, bind_dict, bind) + if not mod_cmd: + filter_bind(uzbl, bind_dict, bind) + return False args = [keycmd[len(glob):],] elif keycmd != glob: - filter_bind(uzbl, bind_dict, bind) + if not mod_cmd: + filter_bind(uzbl, bind_dict, bind) + return False else: args = [] - execindex = len(bind.stack)-1 - if execindex == depth == 0: + if bind.is_global or (not more and depth == 0): exec_bind(uzbl, bind, *args) if not has_args: - uzbl.clear_keycmd() + uzbl.clear_current() return True - elif depth != execindex: + elif more: if bind_dict['depth'] == depth: - bind_dict['filter'] = [bind,] + globalcmds = [cmd for cmd in bind_dict['binds'] if cmd.is_global] + bind_dict['filter'] = [bind,] + globalcmds bind_dict['args'] += args bind_dict['depth'] = depth + 1 - else: - if bind not in bind_dict['filter']: - bind_dict['filter'].append(bind) + elif bind not in bind_dict['filter']: + bind_dict['filter'].append(bind) set_stack_mode(uzbl, bind.prompts[depth]) return False args = bind_dict['args'] + args exec_bind(uzbl, bind, *args) - if on_exec: - uzbl.set_mode() + uzbl.set_mode() + if not has_args: + uzbl.clear_current() return True def keycmd_update(uzbl, keylet): depth = get_stack_depth(uzbl) - keycmd = keylet.to_string() + keycmd = keylet.get_keycmd() for bind in get_filtered_binds(uzbl): - t = bind.stack[depth] + t = bind[depth] if t[MOD_CMD] or t[ON_EXEC]: continue - match_and_exec(uzbl, bind, depth, keycmd) + if match_and_exec(uzbl, bind, depth, keycmd): + return def keycmd_exec(uzbl, keylet): depth = get_stack_depth(uzbl) - keycmd = keylet.to_string() + keycmd = keylet.get_keycmd() for bind in get_filtered_binds(uzbl): - t = bind.stack[depth] + t = bind[depth] if t[MOD_CMD] or not t[ON_EXEC]: continue - match_and_exec(uzbl, bind, depth, keycmd) + if match_and_exec(uzbl, bind, depth, keycmd): + return uzbl.clear_keycmd() def modcmd_update(uzbl, keylet): depth = get_stack_depth(uzbl) - keycmd = keylet.to_string() + keycmd = keylet.get_modcmd() for bind in get_filtered_binds(uzbl): - t = bind.stack[depth] + t = bind[depth] if not t[MOD_CMD] or t[ON_EXEC]: continue - match_and_exec(uzbl, bind, depth, keycmd) + if match_and_exec(uzbl, bind, depth, keycmd): + return def modcmd_exec(uzbl, keylet): depth = get_stack_depth(uzbl) - keycmd = keylet.to_string() + keycmd = keylet.get_modcmd() for bind in get_filtered_binds(uzbl): - t = bind.stack[depth] + t = bind[depth] if not t[MOD_CMD] or not t[ON_EXEC]: continue - match_and_exec(uzbl, bind, depth, keycmd) + if match_and_exec(uzbl, bind, depth, keycmd): + return uzbl.clear_modcmd() def init(uzbl): diff --git a/examples/data/uzbl/plugins/keycmd.py b/examples/data/uzbl/plugins/keycmd.py index 3dd6f37..109bb38 100644 --- a/examples/data/uzbl/plugins/keycmd.py +++ b/examples/data/uzbl/plugins/keycmd.py @@ -1,16 +1,14 @@ 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 = {} +__export__ = ['clear_keycmd', 'set_keycmd', 'set_cursor_pos', 'get_keylet', + 'clear_current', 'clear_modcmd'] # Hold the keylets. UZBLS = {} # Simple key names map. -_SIMPLEKEYS = { +SIMPLEKEYS = { 'Control': 'Ctrl', 'ISO_Left_Tab': 'Shift-Tab', 'space':'Space', @@ -20,7 +18,7 @@ _SIMPLEKEYS = { KEYCMD_FORMAT = "%s<span @cursor_style>%s</span>%s" -def escape(str): +def uzbl_escape(str): '''Prevent outgoing keycmd values from expanding inside the status_format.''' @@ -34,58 +32,57 @@ def escape(str): 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 + # Modcmd tracking self.held = [] + self.modcmd = '' + self.is_modcmd = False - # to_string() string building cache. - self._to_string = None + # Keycmd tracking + self.keycmd = '' + self.cursor = 0 - self.modcmd = False - self.wasmod = False + # Keylet string repr cache. + self._repr_cache = None - def __repr__(self): - return '<Keycmd(%r)>' % self.to_string() + def get_keycmd(self): + '''Get the keycmd-part of the keylet.''' + + return self.keycmd - 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 + def get_modcmd(self): + '''Get the modcmd-part of the keylet.''' - if not self.held: - self._to_string = self.cmd + if not self.is_modcmd: + return '' - else: - self._to_string = ''.join(['<%s>' % key for key in self.held]) - if self.cmd: - self._to_string += self.cmd + return ''.join(['<%s>' % key for key in self.held]) + self.modcmd + + + def __repr__(self): + '''Return a string representation of the keylet.''' - return self._to_string + if self._repr_cache: + return self._repr_cache + l = [] + if self.is_modcmd: + l.append('modcmd=%r' % self.get_modcmd()) - def match(self, regex): - '''See if the keycmd string matches the given regex.''' + elif self.held: + l.append('held=%r' % ''.join(['<%s>'%key for key in self.held])) - return bool(get_regex(regex)(self.to_string())) + if self.keycmd: + l.append('keycmd=%r' % self.get_keycmd()) + + self._repr_cache = '<Keylet(%s)>' % ', '.join(l) + return self._repr_cache def make_simple(key): @@ -95,8 +92,8 @@ def make_simple(key): if key.endswith('_L') or key.endswith('_R'): key = key[:-2] - if key in _SIMPLEKEYS: - key = _SIMPLEKEYS[key] + if key in SIMPLEKEYS: + key = SIMPLEKEYS[key] return key @@ -123,7 +120,7 @@ def get_keylet(uzbl): add_instance(uzbl) keylet = UZBLS[uzbl] - keylet._to_string = None + keylet._repr_cache = False return keylet @@ -131,75 +128,102 @@ def clear_keycmd(uzbl): '''Clear the keycmd for this uzbl instance.''' k = get_keylet(uzbl) - k.cmd = '' + k.keycmd = '' k.cursor = 0 - k._to_string = None - - if k.modcmd: - k.wasmod = True - - k.modcmd = False + k._repr_cache = False config = uzbl.get_config() if 'keycmd' not in config or config['keycmd'] != '': - config['keycmd'] = '' + uzbl.set('keycmd', '') + uzbl.send('update_gui') uzbl.event('KEYCMD_CLEAR') -def update_event(uzbl, k): +def clear_modcmd(uzbl, clear_held=False): + '''Clear the modcmd for this uzbl instance.''' + + k = get_keylet(uzbl) + k.modcmd = '' + k.is_modcmd = False + k._repr_cache = False + if clear_held: + k.held = [] + + config = uzbl.get_config() + if 'modcmd' not in config or config['modcmd'] != '': + uzbl.set('modcmd', '') + uzbl.send('update_gui') + + uzbl.event('MODCMD_CLEAR') + + +def clear_current(uzbl): + '''Clear the modcmd if is_modcmd else clear keycmd.''' + + k = get_keylet(uzbl) + if k.is_modcmd: + clear_modcmd(uzbl) + + else: + clear_keycmd(uzbl) + + +def focus_changed(uzbl, *args): + '''Focus to the uzbl instance has now been lost which means all currently + held keys in the held list will not get a KEY_RELEASE event so clear the + entire held list.''' + + clear_modcmd(uzbl, clear_held=True) + + +def update_event(uzbl, k, execute=True): '''Raise keycmd & modcmd update events.''' config = uzbl.get_config() - if k.modcmd: - keycmd = k.to_string() + keycmd, modcmd = k.get_keycmd(), k.get_modcmd() + + if k.is_modcmd: uzbl.event('MODCMD_UPDATE', k) - if keycmd != k.to_string(): - return - if 'modcmd_updates' in config and config['modcmd_updates'] != '1': - return + else: + uzbl.event('KEYCMD_UPDATE', k) - return uzbl.set('keycmd', escape(keycmd)) + if 'modcmd_updates' not in config or config['modcmd_updates'] == '1': + new_modcmd = k.get_modcmd() + if not new_modcmd or new_modcmd == modcmd: + uzbl.set('modcmd', uzbl_escape(new_modcmd)) if 'keycmd_events' in config and config['keycmd_events'] != '1': - return + return uzbl.send('update_gui') - keycmd = k.cmd - uzbl.event('KEYCMD_UPDATE', k) - if keycmd != k.cmd: - return + new_keycmd = k.get_keycmd() + if not new_keycmd or new_keycmd != keycmd: + uzbl.set('keycmd', '') + return uzbl.send('update_gui') - 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] + 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))) + uzbl.send('update_gui') - else: - cursor = ' ' - chunks = map(escape, [k.cmd[:k.cursor], cursor, k.cmd[k.cursor+1:]]) - uzbl.set('keycmd', KEYCMD_FORMAT % tuple(chunks)) +def inject_char(str, index, char): + '''Inject character into string at at given index.''' + + assert len(char) == 1 + return "%s%s%s" % (str[:index], char, str[index:]) 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. <Ctrl>+t, clear & - <Ctrl>+o without having to re-press <Ctrl>) 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. + a. append char to keycmd + 4. If not in modcmd mode and a modkey was pressed set modcmd mode. + 5. If in modcmd mode the pressed key is added to the held keys list. 6. Keycmd is updated and events raised if anything is changed.''' if key.startswith('Shift_'): @@ -209,115 +233,48 @@ def key_press(uzbl, key): 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:]) + if key == 'Space' and not k.held and k.keycmd: + k.keycmd = inject_char(k.keycmd, 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): + elif not k.held and len(key) == 1: + config = uzbl.get_config() + if 'keycmd_events' not in config or config['keycmd_events'] == '1': + k.keycmd = inject_char(k.keycmd, k.cursor, key) 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: + elif k.keycmd: + k.keycmd = '' 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 len(key) > 1: + k.is_modcmd = True + if key == 'Shift-Tab' and 'Tab' in k.held: + k.held.remove('Tab') - 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 + if key not in k.held: + k.held.append(key) + k.held.sort() 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 + k.is_modcmd = True + k.modcmd += key - if cmdmod: - update_event(uzbl, k) + 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. + 2. If in a mod-command then raise a MODCMD_EXEC. + 3. Check if any modkey is held, if so set modcmd mode. 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' @@ -325,48 +282,98 @@ def key_release(uzbl, key): key = 'Meta' if key in k.held: - if k.modcmd: + if k.is_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) + #k.is_modcmd = False + #k.modcmd = '' + #update_event(uzbl, k) + clear_modcmd(uzbl) 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.keycmd = keycmd + k._repr_cache = None k.cursor = len(keycmd) - update_event(uzbl, k) + update_event(uzbl, k, False) + + +def keycmd_strip_word(uzbl, sep): + ''' Removes the last word from the keycmd, similar to readline ^W ''' + + sep = sep or ' ' + k = get_keylet(uzbl) + if not k.keycmd: + return + + head, tail = k.keycmd[:k.cursor].rstrip(sep), k.keycmd[k.cursor:] + rfind = head.rfind(sep) + head = head[:rfind] if rfind + 1 else '' + k.keycmd = head + tail + k.cursor = len(head) + update_event(uzbl, k, False) + + +def keycmd_backspace(uzbl, *args): + '''Removes the character at the cursor position in the keycmd.''' + + k = get_keylet(uzbl) + if not k.keycmd: + return + + k.keycmd = k.keycmd[:k.cursor-1] + k.keycmd[k.cursor:] + k.cursor -= 1 + update_event(uzbl, k, False) + + +def keycmd_delete(uzbl, *args): + '''Removes the character after the cursor position in the keycmd.''' + + k = get_keylet(uzbl) + if not k.keycmd: + return + + k.keycmd = k.keycmd[:k.cursor] + k.keycmd[k.cursor+1:] + update_event(uzbl, k, False) + + +def keycmd_exec_current(uzbl, *args): + '''Raise a KEYCMD_EXEC with the current keylet and then clear the + keycmd.''' + + k = get_keylet(uzbl) + uzbl.event('KEYCMD_EXEC', k) + clear_keycmd(uzbl) def set_cursor_pos(uzbl, index): '''Allow setting of the cursor position externally. Supports negative - indexing.''' + indexing and relative stepping with '+' and '-'.''' - cursor = int(index.strip()) k = get_keylet(uzbl) + if index == '-': + cursor = k.cursor - 1 - if cursor < 0: - cursor = len(k.cmd) + cursor + elif index == '+': + cursor = k.cursor + 1 + + else: + cursor = int(index.strip()) + if cursor < 0: + cursor = len(k.keycmd) + cursor + 1 if cursor < 0: cursor = 0 - if cursor > len(k.cmd): - cursor = len(k.cmd) + if cursor > len(k.keycmd): + cursor = len(k.keycmd) k.cursor = cursor - update_event(uzbl, k) + update_event(uzbl, k, False) def init(uzbl): @@ -377,6 +384,12 @@ def init(uzbl): 'KEY_PRESS': key_press, 'KEY_RELEASE': key_release, 'SET_KEYCMD': set_keycmd, - 'SET_CURSOR_POS': set_cursor_pos} + 'KEYCMD_STRIP_WORD': keycmd_strip_word, + 'KEYCMD_BACKSPACE': keycmd_backspace, + 'KEYCMD_DELETE': keycmd_delete, + 'KEYCMD_EXEC_CURRENT': keycmd_exec_current, + 'SET_CURSOR_POS': set_cursor_pos, + 'FOCUS_LOST': focus_changed, + 'FOCUS_GAINED': focus_changed} uzbl.connect_dict(connects) diff --git a/examples/data/uzbl/scripts/event_manager.py b/examples/data/uzbl/scripts/event_manager.py index 271c65e..391fb84 100755 --- a/examples/data/uzbl/scripts/event_manager.py +++ b/examples/data/uzbl/scripts/event_manager.py @@ -595,7 +595,13 @@ class UzblEventDaemon(dict): try: uzbl = self['uzbls'][client] - raw = unicode(client.recv(8192), 'utf-8', 'ignore') + 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) diff --git a/uzbl-browser b/uzbl-browser index b1205ac..1084291 100755 --- a/uzbl-browser +++ b/uzbl-browser @@ -1,13 +1,15 @@ #!/bin/sh -# this script implements are more useful "browsing experience". -# We are assuming you want to use the event_manager.py and cookie_daemon.py. -# So, you must have them in the appropriate place, and cookie_daemon_socket must be configured in the default location +# this script implements a more useful out-of-the-box "browsing experience". +# it does so by combining uzbl-core with a set of "recommended" tools and practices. +# see docs for more info +# If you want to customize the behavior of the cookie-daemon or similar helper tools, +# copy them to your $XDG_DATA_HOME/uzbl/scripts/, edit them and update $PATH # Also, we assume existence of fifo/socket == correctly functioning cookie_daemon/event_manager. # Checking correct functioning of the daemons here would be too complex here, and it's not implemented in uzbl-core either. # But this shouldn't cause much problems.. - +PREFIX=/usr/local if [ -z "$XDG_DATA_HOME" ] then export XDG_DATA_HOME=$HOME/.local/share @@ -18,14 +20,37 @@ then export XDG_CACHE_HOME=$HOME/.cache fi -if [ ! -S $XDG_CACHE_HOME/uzbl/cookie_daemon_socket ] +if [ -z "$XDG_CONFIG_HOME" ] then - if [ -f "$XDG_DATA_HOME/uzbl/scripts/cookie_daemon.py" ] + export XDG_CONFIG_HOME=$HOME/.config +fi + +# assure the relevant directories exist. +for dir in $XDG_CACHE_HOME/uzbl $XDG_DATA_HOME/uzbl $XDG_CONFIG_HOME/uzbl +do + if [ ! -d $dir ] then - $XDG_DATA_HOME/uzbl/scripts/cookie_daemon.py - else - /usr/local/share/uzbl/examples/data/uzbl/scripts/cookie_daemon.py + if ! mkdir -p $dir + then + echo "could not create $dir" >&2 + exit 2 + fi + # if we're initialising a new config directory, put the default (recommended) config in it + if [ "$dir" == $XDG_CONFIG_HOME/uzbl ] + then + if ! cp $PREFIX/share/uzbl/examples/config/uzbl/config $XDG_CONFIG_HOME/uzbl/config + then + echo "Could not copy default config to $XDG_CONFIG_HOME/uzbl/config" >&2 + exit 3 + fi + fi fi +done + +if [ ! -S $XDG_CACHE_HOME/uzbl/cookie_daemon_socket ] +then + # if you want to customize it, copy to your $XDG_DATA_HOME/uzbl/scripts/ and update $PATH + cookie_daemon.py fi DAEMON_SOCKET=$XDG_CACHE_HOME/uzbl/event_daemon @@ -33,7 +58,7 @@ DAEMON_PID=$XDG_CACHE_HOME/uzbl/event_daemon.pid #if [ -f "$DAEMON_PID" ] #then - uzbl-daemon start + event_manager.py -v start #fi -uzbl-core "$@" --connect-socket $DAEMON_SOCKET 1>/dev/null +uzbl-core "$@" --connect-socket $DAEMON_SOCKET | grep -v ^EVENT diff --git a/uzbl-core.c b/uzbl-core.c index 4fe08ce..3ece965 100644 --- a/uzbl-core.c +++ b/uzbl-core.c @@ -1229,8 +1229,9 @@ void spawn(WebKitWebView *web_view, GArray *argv, GString *result) { (void)web_view; (void)result; gchar *path = NULL; + //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after - if ( argv_idx(argv, 0) && + if (argv_idx(argv, 0) && ((path = find_existing_file(argv_idx(argv, 0)))) ) { run_command(path, 0, ((const gchar **) (argv->data + sizeof(gchar*))), @@ -1242,10 +1243,15 @@ spawn(WebKitWebView *web_view, GArray *argv, GString *result) { void spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) { (void)web_view; (void)result; + gchar *path = NULL; - if (argv_idx(argv, 0)) - run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), + if (argv_idx(argv, 0) && + ((path = find_existing_file(argv_idx(argv, 0)))) ) { + run_command(path, 0, + ((const gchar **) (argv->data + sizeof(gchar*))), TRUE, &uzbl.comm.sync_stdout); + g_free(path); + } } void diff --git a/uzbl-daemon b/uzbl-daemon deleted file mode 100755 index 0e4c0e1..0000000 --- a/uzbl-daemon +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# TODO: Fix up the launcher to check the following paths in order to find the -# correct event_manager.py to run: -# 1. $XDG_DATA_HOME/uzbl/scripts/event_manager.py -# 2. /usr/local/share/uzbl/examples/data/uzbl/scripts/event_manager.py - -if [ -z "$XDG_DATA_HOME" ] -then - XDG_DATA_HOME=$HOME/.local/share -fi - -if [ -z "$XDG_CACHE_HOME" ] -then - XDG_CACHE_HOME=$HOME/.cache -fi - -EVENT_MANAGER=/usr/local/share/uzbl/examples/data/uzbl/scripts/event_manager.py - -$EVENT_MANAGER -v "$@" |