aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brendan Taylor <whateley@gmail.com>2009-10-25 14:00:40 -0600
committerGravatar Brendan Taylor <whateley@gmail.com>2009-10-25 14:00:40 -0600
commitd8264b1ed147aa397f4f89450c6bd980aae0503c (patch)
tree9741ac33a3d4aa5a51d6c212cd2b623f059b7d2c
parent7ca43bde2de171f4378049dff54691542bf3b1e6 (diff)
parente3cb0cc1f85a9de67b61c7c052bec9d8357fac0c (diff)
Merge remote branch 'dieterbe/experimental'
-rw-r--r--Makefile24
-rw-r--r--examples/config/uzbl/config44
-rw-r--r--examples/data/uzbl/plugins/bind.py97
-rw-r--r--examples/data/uzbl/plugins/keycmd.py395
-rwxr-xr-xexamples/data/uzbl/scripts/event_manager.py8
-rwxr-xr-xuzbl-browser47
-rw-r--r--uzbl-core.c12
-rwxr-xr-xuzbl-daemon20
8 files changed, 368 insertions, 279 deletions
diff --git a/Makefile b/Makefile
index 71bd0f5..6fe6f2c 100644
--- a/Makefile
+++ b/Makefile
@@ -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 "$@"