aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-xexamples/data/uzbl/scripts/event_manager.py513
-rw-r--r--examples/data/uzbl/scripts/plugins/bind.py49
-rw-r--r--examples/data/uzbl/scripts/plugins/dump_config.py11
-rw-r--r--examples/data/uzbl/scripts/plugins/echo_keys.py16
-rw-r--r--uzbl-core.c155
-rw-r--r--uzbl-core.h14
6 files changed, 630 insertions, 128 deletions
diff --git a/examples/data/uzbl/scripts/event_manager.py b/examples/data/uzbl/scripts/event_manager.py
index d6f4a36..5145653 100755
--- a/examples/data/uzbl/scripts/event_manager.py
+++ b/examples/data/uzbl/scripts/event_manager.py
@@ -1,113 +1,498 @@
#!/usr/bin/env python
-# Uzbl sample event manager
+# Event Manager for Uzbl
+# Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com>
+# Copyright (c) 2009, Dieter Plaetinck <diterer@plaetinck.be>
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
+# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
-The Python Event Manager
-========================
-Sample event manager written in python
+E V E N T _ M A N A G E R . P Y
+===============================
+
+Event manager for uzbl written in python.
Usage
+=====
+
+ uzbl | $XDG_DATA_HOME/uzbl/scripts/event_manager.py
+
+Todo
====
-uzbl | <path to event_manager.py>
+
+ - Command line options including supplying a list of plugins to load or not
+ load (default is load all plugins in the plugin_dir).
+ - Spell checking.
+
'''
-import sys
+import imp
import os
+import sys
+import select
+import re
+import types
+import socket
+import pprint
+from traceback import print_exc
-# config dir. needed for bindings config
-if 'XDG_CONFIG_HOME' in os.environ.keys() and os.environ['XDG_CONFIG_HOME']:
- CONFIG_DIR = os.path.join(os.environ['XDG_CONFIG_HOME'], 'uzbl/')
-else:
- CONFIG_DIR = os.path.join(os.environ['HOME'], '.config/uzbl/')
+# ============================================================================
+# ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
+# ============================================================================
-# Default config
-config = {
- 'uzbl_fifo': '',
- 'verbose': True,
+def xdghome(key, default):
+ '''Attempts to use the environ XDG_*_HOME paths if they exist otherwise
+ use $HOME and the default path.'''
-} # End of config dictionary.
+ xdgkey = "XDG_%s_HOME" % key
+ if xdgkey in os.environ.keys() and os.environ[xdgkey]:
+ return os.environ[xdgkey]
-# buffer for building up commands
-keycmd = ''
+ return os.path.join(os.environ['HOME'], default)
+# Setup xdg paths.
+DATA_DIR = os.path.join(xdghome('DATA', '.local/share/'), 'uzbl/')
+# Config dict (NOT the same as the uzbl.config).
+config = {
+ 'verbose': True,
+ 'plugin_dir': "$XDG_DATA_HOME/uzbl/scripts/plugins/"
+}
+
+
+# ============================================================================
+# ::: End of configuration section :::::::::::::::::::::::::::::::::::::::::::
+# ============================================================================
+
+
+# Define some globals.
+_VALIDSETKEY = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$").match
_SCRIPTNAME = os.path.basename(sys.argv[0])
+_TYPECONVERT = {'int': int, 'float': float, 'str': str}
+
def echo(msg):
'''Prints only if the verbose flag has been set.'''
if config['verbose']:
- sys.stderr.write("%s: %s\n" % (_SCRIPTNAME, msg))
+ sys.stdout.write("%s: %s\n" % (_SCRIPTNAME, msg))
+
+
+def counter():
+ '''Generate unique object id's.'''
+
+ i = 0
+ while True:
+ i += 1
+ yield i
+
+
+class PluginManager(dict):
+ def __init__(self):
+
+ plugin_dir = os.path.expandvars(config['plugin_dir'])
+ self.plugin_dir = os.path.realpath(plugin_dir)
+ if not os.path.exists(self.plugin_dir):
+ os.makedirs(self.plugin_dir)
+
+ self.load_plugins()
+
+
+ def _find_plugins(self):
+ '''Find all python scripts in plugin dir and return a list of
+ locations and imp moduleinfo's.'''
+
+ dirlist = os.listdir(self.plugin_dir)
+ pythonfiles = filter(lambda s: s.endswith('.py'), dirlist)
+
+ plugins = []
+
+ for filename in pythonfiles:
+ plugins.append(filename[:-3])
+
+ return plugins
+
+
+ def _unload_plugin(self, plugin, remove_pyc=True):
+ '''Unload specific plugin and remove all waste in sys.modules
+
+ Notice: manual manipulation of sys.modules is very un-pythonic but I
+ see no other way to make sure you have 100% unloaded the module. Also
+ this allows us to implement a reload plugins function.'''
+
+ allmodules = sys.modules.keys()
+ allrefs = filter(lambda s: s.startswith("%s." % plugin), allmodules)
+
+ for ref in allrefs:
+ del sys.modules[ref]
+
+ if plugin in sys.modules.keys():
+ del sys.modules[plugin]
+
+ if plugin in self.keys():
+ dict.__delitem__(self, plugin)
+
+ if remove_pyc:
+ pyc = os.path.join(self.plugin_dir, '%s.pyc' % plugin)
+ if os.path.exists(pyc):
+ os.remove(pyc)
+
+
+ def load_plugins(self):
+
+ pluginlist = self._find_plugins()
+
+ for name in pluginlist:
+ try:
+ # Make sure the plugin isn't already loaded.
+ self._unload_plugin(name)
+
+ except:
+ print_exc()
+
+ try:
+ moduleinfo = imp.find_module(name, [self.plugin_dir,])
+ plugin = imp.load_module(name, *moduleinfo)
+ dict.__setitem__(self, name, plugin)
+
+ # Check it has the init function.
+ if not hasattr(plugin, 'init'):
+ raise ImportError('plugin missing main "init" function.')
+
+ except:
+ print_exc()
+ self._unload_plugin(name)
+
+ if len(self.keys()):
+ echo("loaded plugin(s): %s" % ', '.join(self.keys()))
+
+
+ def reload_plugins(self):
+ '''Unload all loaded plugins then run load_plugins() again.
+
+ IMPORTANT: It is crucial that the event handler be deleted if you
+ are going to unload any modules because there is now way to track
+ which module created wich handler.'''
+
+ for plugin in self.keys():
+ self._unload_plugin(plugin)
+
+ self.load_plugins()
+
+
+def export_wrapper(uzbl, function):
+ '''Return an object that appends the uzbl meta-instance to the front of
+ the argument queue. I.e. (*args, **kargs) -> (uzbl, *args, **kargs)'''
+
+ class Export(object):
+ def __init__(self, uzbl, function):
+ self.function = function
+ self.uzbl = uzbl
+
+ def call(self, *args, **kargs):
+ return self.function(self.uzbl, *args, **kargs)
+
+ return Export(uzbl, function).call
+
+
+class UzblInstance(object):
+ '''Event manager for a uzbl instance.'''
+
+ # Singleton plugin manager.
+ plugins = None
+
+ def __init__(self):
+ '''Initialise event manager.'''
+
+ # Hold functions exported by plugins.
+ self._exports = {}
+
+ class ConfigDict(dict):
+ def __init__(self, setcmd):
+ self._setcmd = setcmd
+
+ def __setitem__(self, key, value):
+ '''Updates the config dict and relays any changes back to the
+ uzbl instance via the set function.'''
+
+ if type(value) == types.BooleanType:
+ value = int(value)
+
+ if key in self.keys() and type(value) != type(self[key]):
+ raise TypeError("%r for %r" % (type(value), key))
+
+ else:
+ # All custom variables are strings.
+ value = "" if value is None else str(value)
+
+ self._setcmd(key, value)
+ dict.__setitem__(self, key, value)
+
+ self._config = ConfigDict(self.set)
+ self._running = None
+
+ self._cmdbuffer = []
+ self.keysheld = []
+ self.metaheld = []
+ self.mode = "command"
+ self.binds = {}
+ self.handlers = {}
+ self.nexthid = counter().next
-def error(msg):
- '''Prints error message and exits.'''
+ # Variables needed for fifo & socket communication with uzbl.
+ self.uzbl_fifo = None
+ self.uzbl_socket = None
+ self._fifo_cmd_queue = []
+ self._socket_cmd_queue = []
+ self._socket = None
+ self.send = self._send_socket
- sys.stderr.write("%s: error: %s\n" % (_SCRIPTNAME, msg))
- sys.exit(1)
+ if not self.plugins:
+ self.plugins = PluginManager()
-def fifo(msg):
- '''Writes commands to uzbl's fifo, if the fifo path is known'''
+ # Call the init() function in every plugin which then setup their
+ # respective hooks (event handlers, binds or timers).
+ self._init_plugins()
- echo ('Fifo msg: ' + msg + '(fifo path: ' + config['uzbl_fifo'] + ')')
- if config['uzbl_fifo']:
- fd = os.open(config['uzbl_fifo'], os.O_WRONLY)
- os.write(fd, msg)
- os.close(fd)
-def submit_keycmd():
- '''Sends the updated keycmd to uzbl, which can render it and stuff'''
+ def __getattribute__(self, name):
+ '''Expose any exported functions before class functions.'''
- fifo ('set keycmd = ' + keycmd)
+ if not name.startswith('_'):
+ exports = object.__getattribute__(self, '_exports')
+ if name in exports:
+ return exports[name]
+ return object.__getattribute__(self, name)
-def main():
- '''Main function.'''
- echo ("Init eventhandler")
+ def _get_config(self):
+ '''Return the uzbl config dictionary.'''
+
+ return self._config
+
+ config = property(_get_config)
+
+
+ def _init_plugins(self):
+ '''Call the init() function in every plugin and expose all exposable
+ functions in the plugins root namespace.'''
+
+ # Map all plugin exports
+ for (name, plugin) in self.plugins.items():
+ for attr in dir(plugin):
+ if not attr.startswith('export_') or attr == 'export_':
+ continue
+
+ obj = getattr(plugin, attr)
+ if type(obj) in [types.LambdaType, types.FunctionType]:
+ obj = export_wrapper(self, obj)
+
+ self._exports[attr[7:]] = obj
+
+ echo("exposed attribute(s): %s" % ', '.join(self._exports.keys()))
+
+ # Now call the init function in all plugins.
+ for (name, plugin) in self.plugins.items():
+ try:
+ plugin.init(self)
+
+ except:
+ print_exc()
+
+
+ def _flush(self):
+ '''Flush messages from the outgoing queue to the uzbl instance.'''
+
+ if len(self._fifo_cmd_queue) and self.uzbl_fifo:
+ if os.path.exists(self.uzbl_fifo):
+ h = open(self.uzbl_fifo, 'w')
+ while len(self._fifo_cmd_queue):
+ msg = self._fifo_cmd_queue.pop(0)
+ print "Sending via fifo: %r" % msg
+ h.write("%s\n" % msg)
+ h.close()
+
+ if len(self._socket_cmd_queue) and self.uzbl_socket:
+ if not self._socket and os.path.exists(self.uzbl_socket):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.uzbl_socket)
+ self._socket = sock
+
+ if self._socket:
+ while len(self._socket_cmd_queue):
+ msg = self._socket_cmd_queue.pop(0)
+ print "Sending via socket: %r" % msg
+ self._socket.send("%s\n" % msg)
+
+
+ def _send_fifo(self, msg):
+ '''Send a command to the uzbl instance via the fifo socket.'''
+
+ self._fifo_cmd_queue.append(msg)
+ self._flush()
+
+
+ def _send_socket(self, msg):
+ '''Send a command to the uzbl instance via the socket file.'''
+
+ self._socket_cmd_queue.append(msg)
+ self._flush()
+
+
+ def connect(self, event, handler, *args, **kargs):
+ '''Connect event with handler and return unique handler id. It goes
+ without saying that if you connect handlers with non-existent events
+ nothing will happen so be careful.
+
+ If you choose the handler may be a uzbl command and upon receiving the
+ event the chosen command will be executed by the uzbl instance.'''
+
+ if event not in self.handlers.keys():
+ self.handlers[event] = {}
+
+ id = self.nexthid()
+ d = {'handler': handler, 'args': args, 'kargs': kargs}
+
+ self.handlers[event][id] = d
+ echo("added handler for %s: %r" % (event, d))
+
+ return id
+
+
+ def remove(self, id):
+ '''Remove connected event handler by unique handler id.'''
+
+ for event in self.handlers.keys():
+ if id in self.handlers[event].keys():
+ echo("removed handler %d" % id)
+ del self.handlers[event][id]
+
+
+ def set(self, key, value):
+ '''Sets key "key" with value "value" in the uzbl instance.'''
+
+ # TODO: Make a real escaping function.
+ escape = str
+
+ if not _VALIDSETKEY(key):
+ raise KeyError("%r" % key)
+
+ if '\n' in value:
+ raise ValueError("invalid character: \\n")
+
+ self.send('set %s = %s' % (key, escape(value)))
+
+
+ def listen_from_fd(self, fd):
+ '''Main loop reading event messages from stdin.'''
+
+ self._running = True
+ while self._running:
+ try:
+ if select.select([fd,], [], [], 1)[0]:
+ self.read_from_fd(fd)
+ continue
+
+ self._flush()
+
+ except KeyboardInterrupt:
+ self._running = False
+ print
+
+ except:
+ print_exc()
+
+
+ def read_from_fd(self, fd):
+ '''Reads incoming event messages from fd.'''
+
+ raw = fd.readline()
+ if not raw:
+ # Read null byte (i.e. uzbl closed).
+ self._running = False
+ return
+
+ msg = raw.strip().split(' ', 3)
+
+ if not msg or msg[0] != "EVENT":
+ # Not an event message
+ return
+
+ event, args = msg[1], msg[3]
+ self.handle_event(event, args)
+
+
+ def handle_event(self, event, args):
+ '''Handle uzbl events internally before dispatch.'''
+
+ print event, args
+
+ if event == 'VARIABLE_SET':
+ l = args.split(' ', 2)
+ if len(l) == 2:
+ l.append("")
+
+ key, type, value = l
+ dict.__setitem__(self._config, key, _TYPECONVERT[type](value))
+
+ elif event == 'FIFO_SET':
+ self.uzbl_fifo = args
+ self._flush()
+
+ elif event == 'SOCKET_SET':
+ self.uzbl_socket = args
+ self._flush()
+
+ elif event == 'SHUTDOWN':
+ for (name, plugin) in self.plugins.items():
+ if hasattr(plugin, "cleanup"):
+ plugin.cleanup(uzbl)
+
+ self.dispatch_event(event, args)
+
+
+ def dispatch_event(self, event, args):
+ '''Now send the event to any event handlers added with the connect
+ function. In other words: handle plugin's event hooks.'''
+
+ if event in self.handlers.keys():
+ for hid in self.handlers[event]:
+ try:
+ handler = self.handlers[event][hid]
+ print "Executing handler:", event, handler
+ self.exc_handler(handler, args)
+
+ except:
+ print_exc()
+
+
+ def exc_handler(self, d, args):
+ '''Handle handler.'''
+
+ if type(d['handler']) == types.FunctionType:
+ handler = d['handler']
+ handler(self, args, *d['args'], **d['kargs'])
- for line in sys.stdin:
- line = line.strip()
- data = line.partition('EVENT ')
- if (data[0] == ""):
- line = data[2]
- echo ("Got event: " + line)
- data = line.partition(' ')
- event_name = data[0]
- event_data = data[2]
else:
- echo ("Non-event: " + line)
- continue
-
- if (event_name == 'FIFO_SET'):
- config['uzbl_fifo'] = event_data.split()[-1]
- elif (event_name == 'KEY_PRESS'):
- # todo: keep a table of pressed modkeys. do we work with Mod[1-4] here or Alt_L and such?
- key = event_data.split()[-1]
- if (key == 'Escape'):
- keycmd = ''
- submit_keycmd
- elif (event_name == 'KEY_RELEASE'):
- #todo : update table of pressed modkeys
- submit_keycmd
+ cmd = d['handler']
+ self.send(cmd)
+
if __name__ == "__main__":
- main()
- \ No newline at end of file
+ uzbl = UzblInstance().listen_from_fd(sys.stdin)
diff --git a/examples/data/uzbl/scripts/plugins/bind.py b/examples/data/uzbl/scripts/plugins/bind.py
new file mode 100644
index 0000000..cf2a1ab
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/bind.py
@@ -0,0 +1,49 @@
+'''Plugin provides support for classic uzbl binds.
+
+For example:
+ bind ZZ = exit -> bind('ZZ', 'exit')
+ bind o _ = uri %s -> bind('o _', 'uri %s')
+ bind fl* = sh 'echo %s' -> bind('fl*', "sh 'echo %s'")
+ bind fl* = -> bind('fl*')
+
+And it is also possible to execute a function on activation:
+ bind('DD', myhandler)
+'''
+
+uzbls = {}
+
+def export_bind(uzbl, glob, cmd=None):
+
+ if uzbl not in uzbls:
+ uzbls[uzbl] = {}
+ binds = uzbls[uzbl]
+
+ if not cmd:
+ if glob in binds.keys():
+ echo("deleted bind: %r" % self.binds[glob])
+ del binds[glob]
+
+ d = {'glob': glob, 'once': True, 'hasargs': True, 'cmd': cmd}
+
+ if glob.endswith('*'):
+ d['pre'] = glob.rstrip('*')
+ d['once'] = False
+
+ elif glob.endswith('_'):
+ d['pre'] = glob.rstrip('_')
+
+ else:
+ d['pre'] = glob
+ d['hasargs'] = False
+
+ binds[glob] = d
+ print "added bind: %r" % d
+
+
+def init(uzbl):
+
+ uzbl.bind("test", lambda _: True)
+
+def cleanup(uzbl):
+ if uzbl in uzbls:
+ del uzbl
diff --git a/examples/data/uzbl/scripts/plugins/dump_config.py b/examples/data/uzbl/scripts/plugins/dump_config.py
new file mode 100644
index 0000000..381dbf2
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/dump_config.py
@@ -0,0 +1,11 @@
+import pprint
+
+def dump_config(uzbl, args):
+ '''Dump the config every time the page finishes loading.'''
+
+ print "%s\n" % pprint.pformat(uzbl.config)
+
+
+def init(uzbl):
+ id = uzbl.connect('LOAD_FINISH', dump_config)
+ print "Dump config id:", id
diff --git a/examples/data/uzbl/scripts/plugins/echo_keys.py b/examples/data/uzbl/scripts/plugins/echo_keys.py
new file mode 100644
index 0000000..e1a1850
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/echo_keys.py
@@ -0,0 +1,16 @@
+def echo_keys(uzbl, key, print_meta=True):
+ '''Prints key-presses to the terminal.'''
+
+ keys_pressed = int(uzbl.config['keys_pressed']) + 1
+ print "You pressed:", key, "Total keys pressed:", keys_pressed
+ uzbl.config['keys_pressed'] = str(keys_pressed)
+
+
+def init(uzbl):
+ '''In this function attach all your event hooks using uzbl.connect and
+ uzbl.bind functions.'''
+
+ id = uzbl.connect('KEY_PRESS', echo_keys)
+ print "echo_keys hook id:", id
+
+ uzbl.config['keys_pressed'] = str(0)
diff --git a/uzbl-core.c b/uzbl-core.c
index d8179df..5dd80a2 100644
--- a/uzbl-core.c
+++ b/uzbl-core.c
@@ -192,29 +192,29 @@ const struct var_name_to_ptr_t {
};
/* Event id to name mapping
- * Event names must be in the same
+ * Event names must be in the same
* order as in 'enum event_type'
*
* TODO: Add more useful events
*/
const char *event_table[LAST_EVENT] = {
- "LOAD_START" ,
- "LOAD_COMMIT" ,
- "LOAD_FINISH" ,
- "LOAD_ERROR" ,
+ "LOAD_START" ,
+ "LOAD_COMMIT" ,
+ "LOAD_FINISH" ,
+ "LOAD_ERROR" ,
"KEY_PRESS" ,
"KEY_RELEASE" ,
- "DOWNLOAD_REQUEST" ,
+ "DOWNLOAD_REQUEST" ,
"COMMAND_EXECUTED" ,
"LINK_HOVER" ,
"TITLE_CHANGED" ,
"GEOMETRY_CHANGED" ,
"WEBINSPECTOR" ,
- "COOKIE" ,
"NEW_WINDOW" ,
"SELECTION_CHANGED",
"VARIABLE_SET",
- "FIFO_SET"
+ "FIFO_SET",
+ "SOCKET_SET"
};
@@ -886,43 +886,44 @@ VIEWFUNC(go_forward)
/* -- command to callback/function map for things we cannot attach to any signals */
struct {const char *key; CommandInfo value;} cmdlist[] =
-{ /* key function no_split */
- { "back", {view_go_back, 0} },
- { "forward", {view_go_forward, 0} },
- { "scroll_vert", {scroll_vert, 0} },
- { "scroll_horz", {scroll_horz, 0} },
- { "scroll_begin", {scroll_begin, 0} },
- { "scroll_end", {scroll_end, 0} },
- { "reload", {view_reload, 0}, },
- { "reload_ign_cache", {view_reload_bypass_cache, 0} },
- { "stop", {view_stop_loading, 0}, },
- { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
- { "zoom_out", {view_zoom_out, 0}, },
- { "toggle_zoom_type", {toggle_zoom_type, 0}, },
- { "uri", {load_uri, TRUE} },
- { "js", {run_js, TRUE} },
- { "script", {run_external_js, 0} },
- { "toggle_status", {toggle_status_cb, 0} },
- { "spawn", {spawn, 0} },
- { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
- { "sh", {spawn_sh, 0} },
- { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
- { "talk_to_socket", {talk_to_socket, 0} },
- { "exit", {close_uzbl, 0} },
- { "search", {search_forward_text, TRUE} },
- { "search_reverse", {search_reverse_text, TRUE} },
- { "dehilight", {dehilight, 0} },
- { "toggle_insert_mode", {toggle_insert_mode, 0} },
- { "set", {set_var, TRUE} },
- //{ "get", {get_var, TRUE} },
- { "bind", {act_bind, TRUE} },
- { "dump_config", {act_dump_config, 0} },
- { "keycmd", {keycmd, TRUE} },
- { "keycmd_nl", {keycmd_nl, TRUE} },
- { "keycmd_bs", {keycmd_bs, 0} },
- { "chain", {chain, 0} },
- { "print", {print, TRUE} },
- { "update_gui", {update_gui, TRUE} }
+{ /* key function no_split */
+ { "back", {view_go_back, 0} },
+ { "forward", {view_go_forward, 0} },
+ { "scroll_vert", {scroll_vert, 0} },
+ { "scroll_horz", {scroll_horz, 0} },
+ { "scroll_begin", {scroll_begin, 0} },
+ { "scroll_end", {scroll_end, 0} },
+ { "reload", {view_reload, 0}, },
+ { "reload_ign_cache", {view_reload_bypass_cache, 0} },
+ { "stop", {view_stop_loading, 0}, },
+ { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
+ { "zoom_out", {view_zoom_out, 0}, },
+ { "toggle_zoom_type", {toggle_zoom_type, 0}, },
+ { "uri", {load_uri, TRUE} },
+ { "js", {run_js, TRUE} },
+ { "script", {run_external_js, 0} },
+ { "toggle_status", {toggle_status_cb, 0} },
+ { "spawn", {spawn, 0} },
+ { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
+ { "sh", {spawn_sh, 0} },
+ { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
+ { "talk_to_socket", {talk_to_socket, 0} },
+ { "exit", {close_uzbl, 0} },
+ { "search", {search_forward_text, TRUE} },
+ { "search_reverse", {search_reverse_text, TRUE} },
+ { "dehilight", {dehilight, 0} },
+ { "toggle_insert_mode", {toggle_insert_mode, 0} },
+ { "set", {set_var, TRUE} },
+ //{ "get", {get_var, TRUE} },
+ { "bind", {act_bind, TRUE} },
+ { "dump_config", {act_dump_config, 0} },
+ { "dump_config_as_events", {act_dump_config_as_events, 0} },
+ { "keycmd", {keycmd, TRUE} },
+ { "keycmd_nl", {keycmd_nl, TRUE} },
+ { "keycmd_bs", {keycmd_bs, 0} },
+ { "chain", {chain, 0} },
+ { "print", {print, TRUE} },
+ { "update_gui", {update_gui, TRUE} }
};
void
@@ -1010,6 +1011,11 @@ act_dump_config() {
}
void
+act_dump_config_as_events() {
+ dump_config_as_events();
+}
+
+void
set_keycmd() {
run_keycmd(FALSE);
update_title();
@@ -1966,25 +1972,31 @@ set_var_value(const gchar *name, gchar *val) {
if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
if(!c->writeable) return FALSE;
+ msg = g_string_new(name);
+
/* check for the variable type */
if (c->type == TYPE_STR) {
buf = expand(val, 0);
g_free(*c->ptr.s);
*c->ptr.s = buf;
- msg = g_string_new(name);
- g_string_append_printf(msg, " %s", buf);
- send_event(VARIABLE_SET, msg->str);
- g_string_free(msg,TRUE);
+ g_string_append_printf(msg, " str %s", buf);
+
} else if(c->type == TYPE_INT) {
buf = expand(val, 0);
*c->ptr.i = (int)strtoul(buf, &endp, 10);
g_free(buf);
+ g_string_append_printf(msg, " int %d", *c->ptr.i);
+
} else if (c->type == TYPE_FLOAT) {
buf = expand(val, 0);
*c->ptr.f = strtod(buf, &endp);
g_free(buf);
+ g_string_append_printf(msg, " float %f", *c->ptr.f);
}
+ send_event(VARIABLE_SET, msg->str);
+ g_string_free(msg,TRUE);
+
/* invoke a command specific function */
if(c->func) c->func();
} else {
@@ -2006,6 +2018,11 @@ set_var_value(const gchar *name, gchar *val) {
*c->ptr.s = buf;
g_hash_table_insert(uzbl.comm.proto_var,
g_strdup(name), (gpointer) c);
+
+ msg = g_string_new(name);
+ g_string_append_printf(msg, " str %s", buf);
+ send_event(VARIABLE_SET, msg->str);
+ g_string_free(msg,TRUE);
}
return TRUE;
}
@@ -2235,6 +2252,7 @@ init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL *
if( (chan = g_io_channel_unix_new(sock)) ) {
g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
uzbl.comm.socket_path = path;
+ send_event(SOCKET_SET, path);
return dir;
}
} else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
@@ -2292,7 +2310,7 @@ configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
(void) event;
retrieve_geometry();
- send_event(GEOMETRY_CHANGED, uzbl.gui.geometry);
+ send_event(GEOMETRY_CHANGED, uzbl.gui.geometry);
return FALSE;
}
@@ -2341,7 +2359,7 @@ key_release_cb (GtkWidget* window, GdkEventKey* event) {
void
run_keycmd(const gboolean key_ret) {
-
+
/* run the keycmd immediately if it isn't incremental and doesn't take args */
Action *act;
gchar *tmp;
@@ -2654,6 +2672,7 @@ void
settings_init () {
State *s = &uzbl.state;
Network *n = &uzbl.net;
+
int i;
for (i = 0; default_config[i].command != NULL; i++) {
parse_cmd_line(default_config[i].command, NULL);
@@ -2699,7 +2718,6 @@ void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data)
g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
if(uzbl.behave.cookie_handler)
run_handler(uzbl.behave.cookie_handler, s->str);
- send_event(COOKIE, s->str);
if(uzbl.behave.cookie_handler &&
uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
@@ -2725,7 +2743,6 @@ save_cookies (SoupMessage *msg, gpointer user_data){
GString *s = g_string_new ("");
g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
run_handler(uzbl.behave.cookie_handler, s->str);
- send_event(COOKIE, s->str);
g_free (cookie);
g_string_free(s, TRUE);
}
@@ -2844,18 +2861,36 @@ dump_var_hash(gpointer k, gpointer v, gpointer ud) {
}
void
-dump_key_hash(gpointer k, gpointer v, gpointer ud) {
+dump_config() {
+ g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
+}
+
+void
+dump_var_hash_as_event(gpointer k, gpointer v, gpointer ud) {
(void) ud;
- Action *a = v;
+ uzbl_cmdprop *c = v;
+ GString *msg;
+
+ if(!c->dump)
+ return;
+
+ /* check for the variable type */
+ msg = g_string_new((char *)k);
+ if (c->type == TYPE_STR) {
+ g_string_append_printf(msg, " str %s", *c->ptr.s ? *c->ptr.s : " ");
+ } else if(c->type == TYPE_INT) {
+ g_string_append_printf(msg, " int %d", *c->ptr.i);
+ } else if (c->type == TYPE_FLOAT) {
+ g_string_append_printf(msg, " float %f", *c->ptr.f);
+ }
- printf("bind %s = %s %s\n", (char *)k ,
- (char *)a->name, a->param?(char *)a->param:"");
+ send_event(VARIABLE_SET, msg->str);
+ g_string_free(msg, TRUE);
}
void
-dump_config() {
- g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
- g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
+dump_config_as_events() {
+ g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash_as_event, NULL);
}
void
diff --git a/uzbl-core.h b/uzbl-core.h
index 842d965..9d22eb0 100644
--- a/uzbl-core.h
+++ b/uzbl-core.h
@@ -188,10 +188,10 @@ typedef void sigfunc(int);
enum event_type {
LOAD_START, LOAD_COMMIT, LOAD_FINISH, LOAD_ERROR,
KEY_PRESS, KEY_RELEASE, DOWNLOAD_REQ, COMMAND_EXECUTED,
- LINK_HOVER, TITLE_CHANGED, GEOMETRY_CHANGED,
- WEBINSPECTOR, COOKIE, NEW_WINDOW, SELECTION_CHANGED,
- VARIABLE_SET, FIFO_SET,
-
+ LINK_HOVER, TITLE_CHANGED, GEOMETRY_CHANGED,
+ WEBINSPECTOR, NEW_WINDOW, SELECTION_CHANGED,
+ VARIABLE_SET, FIFO_SET, SOCKET_SET,
+
/* must be last entry */
LAST_EVENT
};
@@ -467,6 +467,9 @@ void
act_dump_config();
void
+act_dump_config_as_events();
+
+void
dump_var_hash(gpointer k, gpointer v, gpointer ud);
void
@@ -476,6 +479,9 @@ void
dump_config();
void
+dump_config_as_events();
+
+void
retrieve_geometry();
void