aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--AUTHORS81
-rw-r--r--Makefile83
-rw-r--r--Makefile-new-test48
-rw-r--r--README116
-rw-r--r--callbacks.c593
-rw-r--r--callbacks.h190
-rw-r--r--docs/INSTALL2
-rw-r--r--docs/TODO96
-rw-r--r--events.c164
-rw-r--r--events.h33
-rw-r--r--examples/config/uzbl/config272
-rwxr-xr-xexamples/data/uzbl/scripts/event_manager.py728
-rwxr-xr-xexamples/data/uzbl/scripts/instance-select-wmii.sh54
-rw-r--r--examples/data/uzbl/scripts/plugins/bind.py374
-rw-r--r--examples/data/uzbl/scripts/plugins/config.py87
-rw-r--r--examples/data/uzbl/scripts/plugins/keycmd.py382
-rw-r--r--examples/data/uzbl/scripts/plugins/mode.py159
-rw-r--r--examples/data/uzbl/scripts/plugins/on_event.py117
-rw-r--r--examples/data/uzbl/scripts/plugins/plugin_template.py75
-rw-r--r--examples/data/uzbl/scripts/plugins/progress_bar.py158
-rwxr-xr-xexamples/data/uzbl/scripts/session.sh8
-rwxr-xr-xexamples/data/uzbl/scripts/uzbl_tabbed.py2
-rw-r--r--inspector.c103
-rw-r--r--inspector.h7
-rw-r--r--tests/Makefile4
-rw-r--r--tests/test-command.c8
-rw-r--r--tests/test-expand.c8
-rwxr-xr-xuzbl-browser38
-rw-r--r--uzbl-core.c (renamed from uzbl.c)1322
-rw-r--r--uzbl-core.h (renamed from uzbl.h)283
30 files changed, 4117 insertions, 1478 deletions
diff --git a/AUTHORS b/AUTHORS
index d6f9123..f88fc63 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,48 +1,61 @@
+Uzbl would never be what it is today without the help of these people.
-Developers:
- Dieter Plaetinck (Dieter@be) <email is dieter AT plaetinck.be>
- Dusan Popovic (dusanx)
- Michael Walker (Barrucadu) <email is mike AT barrucadu.co.uk>
- Přemysl Hrubý (anydot) <email is dfenze AT gmail.com>
- Robert Manea (robm) <email is rob DOT manea AT gmail DOT com>
- Henri Kemppainen (DuClare) <email is akarinotengoku AT THE DOMAIN OF gmail.com>
-Contributors:
- Zane Ashby (HashBox) - Rewrote FIFO interface. Fixed various bugs.
- (sentientswitch) - Cleaned up code. Added some commands.
- Jan Kolkmeier (jouz) - scrolling, link following
- Evgeny Grablyk - libsoup settings
- Damien Leon - misc
- Peter Suschlik - backwards searching
- (salinasv) - move some variables to heap
- Sylvester Johansson (scj) - form filler script & different take on link follower
- (mxf) - uzblcat
- Mark Nevill - misc patches
- Uli Schlachter (psychon) - basic mime_policy_cb & Makefile patch
- James S Wheaton (uranther) - zoom level, test framework
+Special roles:
+- global/C code management/QA: Dieter, Rob
+- python code management/QA: Mason, Tom
+- website/mailinglist/bugtracker/...: Dieter, Michael W.
+
+In alphabetical order:
+
(bobpaul) - session script patches
- Tom Adams (holizz) - few patches, cookies.py, gtkplug/socket & proof of concept uzbl_tabbed.py, scheme_handler
- neutralinsomniac - load_progress = 0 fix
- Maximilian Gaß (mxey) - several small patches
- Abel Camarillo (00z) - various portability fixes, such as BSD fixes for Makefile and posix shell scripts
+ (dequis) - Uzbl.run, birectional socket, javascript commands
+ (evocallaghan) - tiny patches
(israellevin) - toggle_zoom_type
(kmeaw) - fix for multibyte utf8 characters segfault
- (evocallaghan) - tiny patches
+ (mxf) - uzblcat
+ (neutralinsomniac) - load_progress = 0 fix
+ (salinasv) - move some variables to heap
+ (sentientswitch) - Cleaned up code. Added some commands.
Aaron Griffin (phrakture) - Makefile patches to build on OSX
- Mason Larobina - os.environ.keys() & os.path.join fix in cookies.py, work on uzbl_tabbed.py, cookie_daemon.py
- (dequis) - Uzbl.run, birectional socket, javascript commands
+ Abel Camarillo (00z) - various portability fixes, such as BSD fixes for Makefile and posix shell scripts
+ Andraž 'ruskie' Levstik - font_family patch
+ Barak A. Pearlmutter - typo fix
Brendan Taylor (bct) - various bugfixes, making misc variables much better using expand(), refactoring some internal var stuff
+ Chris Mason - code snippets such as basic cookie handler
Chris van Dijk (quigybo) - work on uzbl_tabbed.py
+ Damien Leon - misc
+ David Keijser - keycmd line editing patch for the keycmd plugin.
+ Devon Jones <devon.jones@gmail.com> - uzbl_tabbed: bring_to_front
+ Dieter Plaetinck (Dieter@be) <dieter AT plaetinck.be> - several contributions
+ Dusan Popovic (dusanx) - many contributions to early uzbl
+ Evgeny Grablyk - libsoup settings
+ Helmut Grohne (helmut) - move void **ptr to union, various fixes
+ Henri Kemppainen (DuClare) <email is akarinotengoku AT THE DOMAIN OF gmail.com> - many contributions, mostly old handler code
+ Jake Probst <jake.probst@gmail.com> - uzbl_tabbed: multiline tablist
+ James S Wheaton (uranther) - zoom level, test framework
+ Jan Kolkmeier (jouz) - scrolling, link following
+ Laurence Withers (lwithers) - talk_to_socket
+ Mark Nevill - misc patches
+ Mason Larobina - os.environ.keys() & os.path.join fix in cookies.py, work on uzbl_tabbed.py, cookie_daemon.py
+ Maximilian Gaß (mxey) - several small patches
+ Michael Fiano (axionix) - added cookie_daemon.py whitelist
+ Michael Walker (Barrucadu) <mike AT barrucadu.co.uk> - contributions to early uzbl
Moritz Lenz - small doc fix
+ Olivier Schwander - auto file:// prepend
+ Paweł Zuzelski (pawelz) - download handler proxy patch
+ Peter Suschlik - backwards searching
+ Přemysl Hrubý (anydot) <email is dfenze AT gmail.com> - several C contributions and cleanups
+ Robert Manea (robm) <email is rob DOT manea AT gmail DOT com> - C code all over the place
Sergey Shepelev (temoto) - doc patch
+ Sylvester Johansson (scj) - form filler script & different take on link follower
Tassilo Horn (tsdh) - $VISUAL patch
- Laurence Withers (lwithers) - talk_to_socket
- Andraž 'ruskie' Levstik - font_family patch
- Helmut Grohne (helmut) - move void **ptr to union, various fixes
- Paweł Zuzelski (pawelz) - download handler proxy patch
- Michael Fiano (axionix) - added cookie_daemon.py whitelist
- Jake Probst <jake.probst@gmail.com> - uzbl_tabbed: multiline tablist
- Devon Jones <devon.jones@gmail.com> - uzbl_tabbed: bring_to_front
+ Thorsten Wilms - logo design
+ Tom Adams (holizz) - few patches, cookies.py, gtkplug/socket & proof of concept uzbl_tabbed.py, scheme_handler
+ Uli Schlachter (psychon) - basic mime_policy_cb & Makefile patch
+ Zane Ashby (HashBox) - Rewrote FIFO interface. Fixed various bugs.
+
+Also, thanks to all people who've posted useful things to the mailing list and/or wiki.
Originaly based on http://trac.webkit.org/browser/trunk/WebKitTools/GtkLauncher/main.c
diff --git a/Makefile b/Makefile
index 2a0982c..167881f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,47 +1,94 @@
# first entries are for gnu make, 2nd for BSD make. see http://lists.uzbl.org/pipermail/uzbl-dev-uzbl.org/2009-July/000177.html
-CFLAGS:=-std=c99 $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT="\"$(shell git log | head -n1 | sed "s/.* //")\"" $(CPPFLAGS) -fPIC -W -Wall -Wextra -pedantic -ggdb3
-CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT='"\""'`git log | head -n1 | sed "s/.* //"`'"\""' $(CPPFLAGS) -fPIC -W -Wall -Wextra -pedantic -ggdb3
+CFLAGS:=-std=c99 $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DCOMMIT="\"$(shell git log | head -n1 | sed "s/.* //")\"" $(CPPFLAGS) -fPIC -W -Wall -Wextra -pedantic -ggdb3
+CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DCOMMIT='"\""'`git log | head -n1 | sed "s/.* //"`'"\""' $(CPPFLAGS) -fPIC -W -Wall -Wextra -pedantic -ggdb3
LDFLAGS:=$(shell pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -pthread $(LDFLAGS)
LDFLAGS!=echo `pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -pthread $(LDFLAGS)
-all: uzbl
+SRC = uzbl-core.c events.c callbacks.c inspector.c
+OBJ = ${SRC:.c=.o}
+
+all: uzbl-browser options
+
+options:
+ @echo
+ @echo BUILD OPTIONS:
+ @echo "CFLAGS = ${CFLAGS}"
+ @echo "LDFLAGS = ${LDFLAGS}"
+ @echo
+ @echo See the README file for usage instructions.
+
+
+.c.o:
+ @echo COMPILING $<
+ @${CC} -c ${CFLAGS} $<
+ @echo ... done.
+
+${OBJ}: uzbl-core.h events.h callbacks.h inspector.h config.h
+
+uzbl-core: ${OBJ}
+ @echo
+ @echo LINKING object files
+ @${CC} -o $@ ${OBJ} ${LDFLAGS}
+ @echo ... done.
+ @echo Stripping binary
+ @strip $@
+ @echo ... done.
+
+
+
+uzbl-browser: uzbl-core
PREFIX?=$(DESTDIR)/usr/local
# When compiling unit tests, compile uzbl as a library first
-tests: uzbl.o
- $(CC) -DUZBL_LIBRARY -shared -Wl uzbl.o -o ./tests/libuzbl.so
+tests: uzbl-core.o uzbl-events.o
+ $(CC) -DUZBL_LIBRARY -shared -Wl uzbl-core.o uzbl-events.o -o ./tests/libuzbl-core.so
cd ./tests/; $(MAKE)
-test: uzbl
- ./uzbl --uri http://www.uzbl.org --verbose
+test: uzbl-core
+ ./uzbl-core --uri http://www.uzbl.org --verbose
-test-dev: uzbl
- XDG_DATA_HOME=./examples/data XDG_CONFIG_HOME=./examples/config ./uzbl --uri http://www.uzbl.org --verbose
+test-browser: uzbl-browser
+ PATH="`pwd`:$$PATH" ./uzbl-browser --uri http://www.uzbl.org --verbose
-test-share: uzbl
- XDG_DATA_HOME=${PREFIX}/share/uzbl/examples/data XDG_CONFIG_HOME=${PREFIX}/share/uzbl/examples/config ./uzbl --uri http://www.uzbl.org --verbose
+test-dev: uzbl-core
+ XDG_DATA_HOME=./examples/data XDG_CONFIG_HOME=./examples/config ./uzbl-core --uri http://www.uzbl.org --verbose
+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-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
+
+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
+
+test-share-browser: uzbl-browser
+ XDG_DATA_HOME=${PREFIX}/share/uzbl/examples/data XDG_CONFIG_HOME=${PREFIX}/share/uzbl/examples/config PATH="`pwd`:$$PATH" ./uzbl-browser --uri http://www.uzbl.org --verbose
clean:
- rm -f uzbl
- rm -f uzbl.o
+ rm -f uzbl-core
+ rm -f uzbl-core.o
+ rm -f events.o
+ rm -f callbacks.o
+ rm -f inspector.o
cd ./tests/; $(MAKE) clean
-install:
+install: all
install -d $(PREFIX)/bin
install -d $(PREFIX)/share/uzbl/docs
install -d $(PREFIX)/share/uzbl/examples
- install -m755 uzbl $(PREFIX)/bin/uzbl
cp -rp docs $(PREFIX)/share/uzbl/
cp -rp config.h $(PREFIX)/share/uzbl/docs/
cp -rp examples $(PREFIX)/share/uzbl/
- install -m644 AUTHORS $(PREFIX)/share/uzbl/docs
- install -m644 README $(PREFIX)/share/uzbl/docs
+ install -m755 uzbl-core $(PREFIX)/bin/uzbl-core
+ install -m755 uzbl-browser $(PREFIX)/bin/uzbl-browser
+ install -m644 AUTHORS $(PREFIX)/share/uzbl/docs
+ install -m644 README $(PREFIX)/share/uzbl/docs
uninstall:
- rm -rf $(PREFIX)/bin/uzbl
+ rm -rf $(PREFIX)/bin/uzbl-*
rm -rf $(PREFIX)/share/uzbl
+
diff --git a/Makefile-new-test b/Makefile-new-test
deleted file mode 100644
index a1efa56..0000000
--- a/Makefile-new-test
+++ /dev/null
@@ -1,48 +0,0 @@
-LIBS := gtk+-2.0 webkit-1.0
-ARCH := $(shell uname -m)
-COMMIT := $(shell git log | head -n1 | sed "s/.* //")
-DEBUG := -ggdb -Wall -W -DG_ERRORCHECK_MUTEXES
-
-CFLAGS := $(shell --cflags $(LIBS)) $(DEBUG) -DARCH="$(ARCH)" -DCOMMIT="\"$(COMMIT)\""
-LDFLAGS := $(shell --libs $(LIBS)) $(LDFLAGS)
-
-PREFIX ?= $(DESTDIR)/usr
-BINDIR ?= $(PREFIX)/bin
-UZBLDATA ?= $(PREFIX)/share/uzbl
-DOCSDIR ?= $(PREFIX)/share/uzbl/docs
-EXMPLSDIR ?= $(PREFIX)/share/uzbl/examples
-
-all: uzbl
-
-uzbl: uzbl.c uzbl.h config.h
-
-%: %.c
- $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LIBS) -o $@ $<
-
-test: uzbl
- ./uzbl --uri http://www.uzbl.org
-
-test-config: uzbl
- ./uzbl --uri http://www.uzbl.org < examples/configs/sampleconfig-dev
-
-test-config-real: uzbl
- ./uzbl --uri http://www.uzbl.org < $(EXMPLSDIR)/configs/sampleconfig
-
-clean:
- rm -f uzbl
-
-install:
- install -d $(BINDIR)
- install -d $(DOCSDIR)
- install -d $(EXMPLSDIR)
- install -D -m755 uzbl $(BINDIR)/uzbl
- cp -ax docs/* $(DOCSDIR)
- cp -ax config.h $(DOCSDIR)
- cp -ax examples/* $(EXMPLSDIR)
- install -D -m644 AUTHORS $(DOCSDIR)
- install -D -m644 README $(DOCSDIR)
-
-
-uninstall:
- rm -rf $(BINDIR)/uzbl
- rm -rf $(UZBLDATA)
diff --git a/README b/README
index d233101..56228e4 100644
--- a/README
+++ b/README
@@ -2,14 +2,14 @@
* people want a browser that does everything
* people who want a browser with things like a built-in bookmark manager, address bar, forward/back buttons, ...
* people who expect something that works by default. You'll need to read configs and write/edit scripts
-* people who like nothing from this list: mpd, moc, wmii, dwm, awesome, mutt, pine, vim, dmenu, screen, irssi, weechat, bitlbee
+* people who like nothing from this list: mpd, moc, wmii, dwm, awesome, xmonad, mutt, pine, vim, dmenu, screen, irssi, weechat, bitlbee
### TO NEW PEOPLE:
-* invoke uzbl --help
-* to get you started: `XDG_DATA_HOME=/usr/share/uzbl/examples/data XDG_CONFIG_HOME=/usr/share/uzbl/examples/config uzbl --uri www.archlinux.org`
+* invoke uzbl-browser --help
+* to get you started: `XDG_DATA_HOME=/usr/share/uzbl/examples/data XDG_CONFIG_HOME=/usr/share/uzbl/examples/config uzbl-browser --uri www.archlinux.org`
* alternatively, copy the above data to your real `$XDG_DATA_HOME` and `$XDG_CONFIG_HOME` directories
* try and study the sample config, read the readme to find out how it works.
-* You can change the url with commands (if you have setup the appropriate keybinds) but to be most effective it's better to do url editing/changing _outside_ of uzbl.
+* You can change the url with commands (if you have setup the appropriate keybinds) but to be most effective it's better to do url editing/changing _outside_ of uzbl-browser.
Eg, you can use the `load_from_*` dmenu based scripts to pick/edit a url or type a new one.
* If you have questions, you are likely to find answers in the FAQ or in the other documentation.
* documentation is in /usr/share/uzbl/docs
@@ -81,7 +81,7 @@ When uzbl forks a new instance (eg "open in new window") it will use the same co
If you made changes to the configuration at runtime, these are not pased on to the child.
### COMMAND SYNTAX
-Uzbl will read commands via standard input, named fifo pipe (if `fifo_dir` is set) and IPC socket (when `socket_dir` is set).
+Uzbl-browser will read commands via standard input, named fifo pipe (if `fifo_dir` is set) and IPC socket (when `socket_dir` is set).
For convenience, uzbl can also be instructed to read commands from a file on startup by using the `-c` option. Indeed, the config file is nothing more than a list of commands.
Each command starts with the name of the command or an uzbl variable that expands to it. A command is terminated by a newline.
@@ -147,7 +147,7 @@ The following commands are recognized:
* `search <string>`
* `search_reverse <string>`
- search with no string will search for the next/previous occurrence of the string previously searched for
-* `toggle_insert_mode <optional state>`
+* `toggle_insert_mode <optional state>` TODO new plugin based syntax
- if the optional state is 0, disable insert mode. If 1, enable insert mode.
* `dump_config`
- dumps your current config (which may have been changed at runtime) to stdout, in a format you can use to pipe into uzbl again (or use as config file)
@@ -162,6 +162,8 @@ The following commands are recognized:
- if you use `chain` with a handler script which must return some output (such as a cookie handler -- uzbl will wait for and use its output), use sync_spawn or sync_sh instead of spawn or sh in the command that should give the output
* `update_gui`
- update the contents of the status and title bars
+* `event <event_name> [event_details]`
+ - send custom event
### VARIABLES AND CONSTANTS
Uzbl has a lot of internal variables and constants. You can get the values (using the `print` command, see above), and for variables you can also change the value at
@@ -184,14 +186,15 @@ Besides the builtin variables you can also define your own ones and use them in
- status_pbar_pending: character to denote pending % of pageload
- status_pbar_width: width of progressbar
- status_background: color which can be used to override Gtk theme.
- - insert_indicator: string to denote insert mode
- - command_indicator: string to denote command mode
+ - insert_indicator: string to denote insert mode TODO plugin variable
+ - command_indicator: string to denote command mode TODO plugin variable
- title_format_long: titlebar string when no statusbar shown (will be expanded
- title_format_short: titlebar string when statusbar shown (will be expanded)
- icon: path to icon for Gtk
- - insert_mode: whether insert mode is active
- - always_insert_mode: set this to true if you don't like modal (vim-like) interfaces
- - reset_command_mode: automatically revert to command mode on pageload (unless always_insert_mode is set)
+ - forward_keys: whether uzbl-core should send key events to the webkit view
+ - insert_mode: whether insert mode is active TODO explain plugin variable
+ - always_insert_mode: set this to true if you don't like modal (vim-like) interfaces TODO explain plugin variable
+ - reset_command_mode: automatically revert to command mode on pageload (unless always_insert_mode is set) TODO explain plugin variable
- modkey: modkey which can be pressed to activate keybind from inside insert mode
- load_finish_handler
- load_start_handler
@@ -410,6 +413,92 @@ This way, everything is kept private. It also turns Uzbl into a local variable,
Copying the Uzbl object and creating public functions should be taken with care to avoid creating security holes. Keep in mind that the "f" function above would be defined in the `window` object, and as such any javascript in the current page can call it.
+### EVENTS ###
+
+unlike commands, events are not handled in uzbl itself, but are propagated (dispatched) asynchronously through
+a text stream on stdout. You'll usually use uzbl by piping it's output to a so called 'event handler'
+- makes it possible to use whichever language you want for event handling (python, perl, bash, .. you name it).
+ you'll usually send commands (see above) back to uzbl through its fifo or socket
+- keybindings use x keysyms
+- many finegrained events (hover_over_link, key_press, key_down,..)
+- see example event_handler.py
+
+Note: cookie events are not sent to an event handler but handled internally through the cookie handler because of their synchronous nature.
+Cookie events are really something completely different from all other events. maybe someday we'll use http proxies or something for cookies, but
+for now we still use the handler code)
+
+Basically all events have this format:
+
+ EVENT EVENT_NAME [uzbl_instance_name] event_details
+
+
+Reported events and their specific format:
+
+- on start uzbl will generate:
+
+ EVENT INSTANCE_START [uzbl_instance_name] process_id
+
+- on exit:
+
+ EVENT INSTANCE_EXIT [uzbl_instance_name] process_id
+
+- whenever an uzbl variable is set:
+
+ EVENT VARIABLE_SET [uzbl_instance_name] variable_name str|int|float variable_value
+
+ Note: str|int|float denote the type of variable_value
+
+- upon execution of an uzbl command:
+
+ EVENT COMMAND_EXECUTED [uzbl_instance_name] command_name optional_command_arguments
+
+- when the size or position of the uzbl window changes:
+
+ EVENT GEOMETRY_CHANGED [uzbl_instance_name] WIDTHxHEIGHT+X_POSITION+Y_POSITION
+
+- when the fifo and/or the socket path is set or changed:
+
+ EVENT FIFO_SET [uzbl_instance_name] path_to_fifo
+ EVENT SOCKET_SET [uzbl_instance_name] path_to_socket
+
+- when a website is being loaded:
+
+ EVENT LOAD_COMMIT [uzbl_instance_name] uri
+ EVENT LOAD_START [uzbl_instance_name] uri
+ EVENT LOAD_FINISHED [uzbl_instance_name] uri
+ EVENT LOAD_ERROR [uzbl_instance_name] reason_of_error
+
+- when the title of the uzbl window changes:
+
+ EVENT TITLE_CHANGED [uzbl_instance_name] title_name
+
+- when content needs to be downloaded:
+
+ EVENT DOWNLOAD_REQUEST [uzbl_instance_name] download_uri
+
+- when you hover with the mouse over a link:
+
+ EVENT LINK_HOVER [uzbl_instance_name] uri
+
+- when you press or release a key:
+
+ EVENT KEY_PRESS [uzbl_instance_name] key_name
+ EVENT KEY_RELEASE [uzbl_instance_name] key_name
+
+- when you select some text inside the uzbl window:
+
+ EVENT SELECTION_CHANGED [uzbl_instance_name] selected_text
+
+- when a new uzbl window is created:
+
+ EVENT NEW_WINDOW [uzbl_instance_name] uri
+
+- upon opening/closing of the webinspector window:
+
+ EVENT WEBINSPECTOR [uzbl_instance_name] open
+ EVENT WEBINSPECTOR [uzbl_instance_name] close
+
+
### COMMAND LINE ARGUMENTS
uzbl [ uri ]
-u, --uri=URI Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)
@@ -422,7 +511,10 @@ Copying the Uzbl object and creating public functions should be taken with care
--display=DISPLAY X display to use
--help Help
-
+uzbl scheme://address will work as you expect. If you don't provide the
+scheme:// part, it will check if the argument is an existing file in the
+filesystem: if it is, it will prepend file://, if not, it will
+prepend http://.
### BUGS
diff --git a/callbacks.c b/callbacks.c
new file mode 100644
index 0000000..d65d09a
--- /dev/null
+++ b/callbacks.c
@@ -0,0 +1,593 @@
+/*
+ ** Callbacks
+ ** (c) 2009 by Robert Manea et al.
+*/
+
+#include "uzbl-core.h"
+#include "callbacks.h"
+#include "events.h"
+
+
+void
+set_proxy_url() {
+ SoupURI *suri;
+
+ if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
+ soup_session_remove_feature_by_type(uzbl.net.soup_session,
+ (GType) SOUP_SESSION_PROXY_URI);
+ }
+ else {
+ suri = soup_uri_new(uzbl.net.proxy_url);
+ g_object_set(G_OBJECT(uzbl.net.soup_session),
+ SOUP_SESSION_PROXY_URI,
+ suri, NULL);
+ soup_uri_free(suri);
+ }
+ return;
+}
+
+void
+set_icon() {
+ if(file_exists(uzbl.gui.icon)) {
+ if (uzbl.gui.main_window)
+ gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
+ } else {
+ g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
+ }
+}
+
+void
+cmd_set_geometry() {
+ if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
+ if(uzbl.state.verbose)
+ printf("Error in geometry string: %s\n", uzbl.gui.geometry);
+ }
+ /* update geometry var with the actual geometry
+ this is necessary as some WMs don't seem to honour
+ the above setting and we don't want to end up with
+ wrong geometry information
+ */
+ retrieve_geometry();
+}
+
+void
+cmd_set_status() {
+ if (!uzbl.behave.show_status) {
+ gtk_widget_hide(uzbl.gui.mainbar);
+ } else {
+ gtk_widget_show(uzbl.gui.mainbar);
+ }
+ update_title();
+}
+
+void
+cmd_load_uri() {
+ GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
+ g_array_append_val (a, uzbl.state.uri);
+ load_uri(uzbl.gui.web_view, a, NULL);
+ g_array_free (a, TRUE);
+}
+
+void
+cmd_max_conns() {
+ g_object_set(G_OBJECT(uzbl.net.soup_session),
+ SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
+}
+
+void
+cmd_max_conns_host() {
+ g_object_set(G_OBJECT(uzbl.net.soup_session),
+ SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
+}
+
+void
+cmd_http_debug() {
+ soup_session_remove_feature
+ (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
+ /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
+ /*g_free(uzbl.net.soup_logger);*/
+
+ uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
+ soup_session_add_feature(uzbl.net.soup_session,
+ SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
+}
+
+WebKitWebSettings*
+view_settings() {
+ return webkit_web_view_get_settings(uzbl.gui.web_view);
+}
+
+void
+cmd_font_size() {
+ WebKitWebSettings *ws = view_settings();
+ if (uzbl.behave.font_size > 0) {
+ g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
+ }
+
+ if (uzbl.behave.monospace_size > 0) {
+ g_object_set (G_OBJECT(ws), "default-monospace-font-size",
+ uzbl.behave.monospace_size, NULL);
+ } else {
+ g_object_set (G_OBJECT(ws), "default-monospace-font-size",
+ uzbl.behave.font_size, NULL);
+ }
+}
+
+void
+cmd_default_font_family() {
+ g_object_set (G_OBJECT(view_settings()), "default-font-family",
+ uzbl.behave.default_font_family, NULL);
+}
+
+void
+cmd_monospace_font_family() {
+ g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
+ uzbl.behave.monospace_font_family, NULL);
+}
+
+void
+cmd_sans_serif_font_family() {
+ g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
+ uzbl.behave.sans_serif_font_family, NULL);
+}
+
+void
+cmd_serif_font_family() {
+ g_object_set (G_OBJECT(view_settings()), "serif-font-family",
+ uzbl.behave.serif_font_family, NULL);
+}
+
+void
+cmd_cursive_font_family() {
+ g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
+ uzbl.behave.cursive_font_family, NULL);
+}
+
+void
+cmd_fantasy_font_family() {
+ g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
+ uzbl.behave.fantasy_font_family, NULL);
+}
+
+void
+cmd_zoom_level() {
+ webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
+}
+
+void
+cmd_disable_plugins() {
+ g_object_set (G_OBJECT(view_settings()), "enable-plugins",
+ !uzbl.behave.disable_plugins, NULL);
+}
+
+void
+cmd_disable_scripts() {
+ g_object_set (G_OBJECT(view_settings()), "enable-scripts",
+ !uzbl.behave.disable_scripts, NULL);
+}
+
+void
+cmd_minimum_font_size() {
+ g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
+ uzbl.behave.minimum_font_size, NULL);
+}
+void
+cmd_autoload_img() {
+ g_object_set (G_OBJECT(view_settings()), "auto-load-images",
+ uzbl.behave.autoload_img, NULL);
+}
+
+
+void
+cmd_autoshrink_img() {
+ g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
+ uzbl.behave.autoshrink_img, NULL);
+}
+
+
+void
+cmd_enable_spellcheck() {
+ g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
+ uzbl.behave.enable_spellcheck, NULL);
+}
+
+void
+cmd_enable_private() {
+ g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
+ uzbl.behave.enable_private, NULL);
+}
+
+void
+cmd_print_bg() {
+ g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
+ uzbl.behave.print_bg, NULL);
+}
+
+void
+cmd_style_uri() {
+ g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
+ uzbl.behave.style_uri, NULL);
+}
+
+void
+cmd_resizable_txt() {
+ g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
+ uzbl.behave.resizable_txt, NULL);
+}
+
+void
+cmd_default_encoding() {
+ g_object_set (G_OBJECT(view_settings()), "default-encoding",
+ uzbl.behave.default_encoding, NULL);
+}
+
+void
+cmd_enforce_96dpi() {
+ g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
+ uzbl.behave.enforce_96dpi, NULL);
+}
+
+void
+cmd_caret_browsing() {
+ g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
+ uzbl.behave.caret_browsing, NULL);
+}
+
+void
+cmd_cookie_handler() {
+ gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
+ /* pitfall: doesn't handle chain actions; must the sync_ action manually */
+ if ((g_strcmp0(split[0], "sh") == 0) ||
+ (g_strcmp0(split[0], "spawn") == 0)) {
+ g_free (uzbl.behave.cookie_handler);
+ uzbl.behave.cookie_handler =
+ g_strdup_printf("sync_%s %s", split[0], split[1]);
+ }
+ g_strfreev (split);
+}
+
+void
+cmd_scheme_handler() {
+ gchar **split = g_strsplit(uzbl.behave.scheme_handler, " ", 2);
+ /* pitfall: doesn't handle chain actions; must the sync_ action manually */
+ if ((g_strcmp0(split[0], "sh") == 0) ||
+ (g_strcmp0(split[0], "spawn") == 0)) {
+ g_free (uzbl.behave.scheme_handler);
+ uzbl.behave.scheme_handler =
+ g_strdup_printf("sync_%s %s", split[0], split[1]);
+ }
+ g_strfreev (split);
+}
+
+void
+cmd_fifo_dir() {
+ uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
+}
+
+void
+cmd_socket_dir() {
+ uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
+}
+
+void
+cmd_inject_html() {
+ if(uzbl.behave.inject_html) {
+ webkit_web_view_load_html_string (uzbl.gui.web_view,
+ uzbl.behave.inject_html, NULL);
+ }
+}
+
+void
+cmd_useragent() {
+ if (*uzbl.net.useragent == ' ') {
+ g_free (uzbl.net.useragent);
+ uzbl.net.useragent = NULL;
+ } else {
+ g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
+ uzbl.net.useragent, NULL);
+ }
+}
+
+/* requires webkit >=1.1.14 */
+/*
+void
+cmd_view_source() {
+ webkit_web_view_set_view_source_mode(uzbl.gui.web_view,
+ (gboolean) uzbl.behave.view_source);
+}
+*/
+
+void
+toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
+ (void)page;
+ (void)argv;
+ (void)result;
+
+ webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
+}
+
+void
+toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
+ (void)page;
+ (void)argv;
+ (void)result;
+
+ if (uzbl.behave.show_status) {
+ gtk_widget_hide(uzbl.gui.mainbar);
+ } else {
+ gtk_widget_show(uzbl.gui.mainbar);
+ }
+ uzbl.behave.show_status = !uzbl.behave.show_status;
+ update_title();
+}
+
+void
+link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
+ (void) page;
+ (void) title;
+ (void) data;
+ State *s = &uzbl.state;
+
+ if(s->selected_url) {
+ if(s->last_selected_url)
+ g_free(s->last_selected_url);
+ s->last_selected_url = g_strdup(s->selected_url);
+ }
+ else {
+ if(s->last_selected_url) g_free(s->last_selected_url);
+ s->last_selected_url = NULL;
+ }
+
+ g_free(s->selected_url);
+ s->selected_url = NULL;
+
+ if (link) {
+ s->selected_url = g_strdup(link);
+
+ if(s->last_selected_url &&
+ g_strcmp0(s->selected_url, s->last_selected_url))
+ send_event(LINK_UNHOVER, s->last_selected_url, NULL);
+
+ send_event(LINK_HOVER, s->selected_url, NULL);
+ }
+ else if(s->last_selected_url) {
+ send_event(LINK_UNHOVER, s->last_selected_url, NULL);
+ }
+
+ update_title();
+}
+
+void
+title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
+ (void) web_view;
+ (void) param_spec;
+ const gchar *title = webkit_web_view_get_title(web_view);
+ if (uzbl.gui.main_title)
+ g_free (uzbl.gui.main_title);
+ uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
+ update_title();
+ send_event(TITLE_CHANGED, uzbl.gui.main_title, NULL);
+}
+
+void
+progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
+ (void) page;
+ (void) data;
+ gchar *prg_str;
+
+ prg_str = itos(progress);
+ send_event(LOAD_PROGRESS, prg_str, NULL);
+ g_free(prg_str);
+
+ update_title();
+}
+
+void
+selection_changed_cb(WebKitWebView *webkitwebview, gpointer ud) {
+ (void)ud;
+ gchar *tmp;
+
+ webkit_web_view_copy_clipboard(webkitwebview);
+ tmp = gtk_clipboard_wait_for_text(gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
+ send_event(SELECTION_CHANGED, tmp, NULL);
+ g_free(tmp);
+}
+
+void
+load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
+ (void) page;
+ (void) data;
+
+ if (uzbl.behave.load_finish_handler)
+ run_handler(uzbl.behave.load_finish_handler, "");
+
+ send_event(LOAD_FINISH, webkit_web_frame_get_uri(frame), NULL);
+}
+
+void
+load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
+ (void) page;
+ (void) frame;
+ (void) data;
+ uzbl.gui.sbar.load_progress = 0;
+ if (uzbl.behave.load_start_handler)
+ run_handler(uzbl.behave.load_start_handler, "");
+
+ send_event(LOAD_START, uzbl.state.uri, NULL);
+}
+
+void
+load_error_cb (WebKitWebView* page, WebKitWebFrame* frame, gchar *uri, gpointer web_err, gpointer ud) {
+ (void) page;
+ (void) frame;
+ (void) ud;
+ GError *err = web_err;
+ gchar *details;
+
+ details = g_strdup_printf("%s %d:%s", uri, err->code, err->message);
+ send_event(LOAD_ERROR, details, NULL);
+ g_free(details);
+}
+
+void
+load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
+ (void) page;
+ (void) data;
+ g_free (uzbl.state.uri);
+ GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
+ uzbl.state.uri = g_string_free (newuri, FALSE);
+
+ if (uzbl.behave.load_commit_handler)
+ run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
+
+ /* event message */
+ send_event(LOAD_COMMIT, webkit_web_frame_get_uri (frame), NULL);
+}
+
+void
+destroy_cb (GtkWidget* widget, gpointer data) {
+ (void) widget;
+ (void) data;
+ gtk_main_quit ();
+}
+
+gboolean
+configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
+ (void) window;
+ (void) event;
+
+ retrieve_geometry();
+ send_event(GEOMETRY_CHANGED, uzbl.gui.geometry, NULL);
+ return FALSE;
+}
+
+gboolean
+key_press_cb (GtkWidget* window, GdkEventKey* event) {
+ (void) window;
+
+ if(event->type == GDK_KEY_PRESS)
+ key_to_event(event->keyval, GDK_KEY_PRESS);
+
+ return uzbl.behave.forward_keys ? FALSE : TRUE;
+}
+
+gboolean
+key_release_cb (GtkWidget* window, GdkEventKey* event) {
+ (void) window;
+
+ if(event->type == GDK_KEY_RELEASE)
+ key_to_event(event->keyval, GDK_KEY_RELEASE);
+
+ return TRUE;
+}
+
+gboolean
+navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
+ (void) web_view;
+ (void) frame;
+ (void) navigation_action;
+ (void) user_data;
+
+ const gchar* uri = webkit_network_request_get_uri (request);
+ gboolean decision_made = FALSE;
+
+ if (uzbl.state.verbose)
+ printf("Navigation requested -> %s\n", uri);
+
+ if (uzbl.behave.scheme_handler) {
+ GString *s = g_string_new ("");
+ g_string_printf(s, "'%s'", uri);
+
+ run_handler(uzbl.behave.scheme_handler, s->str);
+
+ if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
+ char *p = strchr(uzbl.comm.sync_stdout, '\n' );
+ if ( p != NULL ) *p = '\0';
+ if (!strcmp(uzbl.comm.sync_stdout, "USED")) {
+ webkit_web_policy_decision_ignore(policy_decision);
+ decision_made = TRUE;
+ }
+ }
+ if (uzbl.comm.sync_stdout)
+ uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+
+ g_string_free(s, TRUE);
+ }
+ if (!decision_made)
+ webkit_web_policy_decision_use(policy_decision);
+
+ return TRUE;
+}
+
+gboolean
+new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
+ (void) web_view;
+ (void) frame;
+ (void) navigation_action;
+ (void) policy_decision;
+ (void) user_data;
+ const gchar* uri = webkit_network_request_get_uri (request);
+ if (uzbl.state.verbose)
+ printf("New window requested -> %s \n", uri);
+ webkit_web_policy_decision_use(policy_decision);
+ send_event(NEW_WINDOW, uri, NULL);
+ return TRUE;
+}
+
+gboolean
+mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
+ (void) frame;
+ (void) request;
+ (void) user_data;
+
+ /* If we can display it, let's display it... */
+ if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
+ webkit_web_policy_decision_use (policy_decision);
+ return TRUE;
+ }
+
+ /* ...everything we can't display is downloaded */
+ webkit_web_policy_decision_download (policy_decision);
+ return TRUE;
+}
+
+/*@null@*/ WebKitWebView*
+create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
+ (void) web_view;
+ (void) frame;
+ (void) user_data;
+ if (uzbl.state.selected_url != NULL) {
+ if (uzbl.state.verbose)
+ printf("\nNew web view -> %s\n",uzbl.state.selected_url);
+ new_window_load_uri(uzbl.state.selected_url);
+ } else {
+ if (uzbl.state.verbose)
+ printf("New web view -> %s\n","Nothing to open, exiting");
+ }
+ return (NULL);
+}
+
+gboolean
+download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
+ (void) web_view;
+ (void) user_data;
+ if (uzbl.behave.download_handler) {
+ const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
+ if (uzbl.state.verbose)
+ printf("Download -> %s\n",uri);
+ /* if urls not escaped, we may have to escape and quote uri before this call */
+
+ GString *args = g_string_new(uri);
+
+ if (uzbl.net.proxy_url) {
+ g_string_append_c(args, ' ');
+ g_string_append(args, uzbl.net.proxy_url);
+ }
+
+ run_handler(uzbl.behave.download_handler, args->str);
+
+ g_string_free(args, TRUE);
+ }
+ send_event(DOWNLOAD_REQ, webkit_download_get_uri ((WebKitDownload*)download), NULL);
+ return (FALSE);
+}
diff --git a/callbacks.h b/callbacks.h
new file mode 100644
index 0000000..16ff48a
--- /dev/null
+++ b/callbacks.h
@@ -0,0 +1,190 @@
+/*
+ ** Callbacks
+ ** (c) 2009 by Robert Manea et al.
+*/
+
+void
+cmd_load_uri();
+
+void
+cmd_set_status();
+
+void
+set_proxy_url();
+
+void
+set_icon();
+
+void
+cmd_cookie_handler();
+
+void
+cmd_scheme_handler();
+
+void
+move_statusbar();
+
+void
+cmd_http_debug();
+
+void
+cmd_max_conns();
+
+void
+cmd_max_conns_host();
+
+/* exported WebKitWebSettings properties */
+void
+cmd_font_size();
+
+void
+cmd_default_font_family();
+
+void
+cmd_monospace_font_family();
+
+void
+cmd_sans_serif_font_family();
+
+void
+cmd_serif_font_family();
+
+void
+cmd_cursive_font_family();
+
+void
+cmd_fantasy_font_family();
+
+void
+cmd_zoom_level();
+
+void
+cmd_disable_plugins();
+
+void
+cmd_disable_scripts();
+
+void
+cmd_minimum_font_size();
+
+void
+cmd_fifo_dir();
+
+void
+cmd_socket_dir();
+
+void
+cmd_useragent() ;
+
+void
+cmd_autoload_img();
+
+void
+cmd_autoshrink_img();
+
+void
+cmd_enable_spellcheck();
+
+void
+cmd_enable_private();
+
+void
+cmd_print_bg();
+
+void
+cmd_style_uri();
+
+void
+cmd_resizable_txt();
+
+void
+cmd_default_encoding();
+
+void
+cmd_enforce_96dpi();
+
+void
+cmd_inject_html();
+
+void
+cmd_caret_browsing();
+
+void
+cmd_set_geometry();
+
+/*
+void
+cmd_view_source();
+*/
+
+void
+cmd_load_start();
+
+WebKitWebSettings*
+view_settings();
+
+gboolean
+download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data);
+
+void
+toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result);
+
+void
+toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result);
+
+void
+link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data);
+
+void
+title_change_cb (WebKitWebView* web_view, GParamSpec param_spec);
+
+void
+progress_change_cb (WebKitWebView* page, gint progress, gpointer data);
+
+void
+load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data);
+
+void
+load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data);
+
+void
+load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data);
+
+void
+load_error_cb (WebKitWebView* page, WebKitWebFrame* frame, gchar *uri, gpointer web_err, gpointer ud);
+
+void
+selection_changed_cb(WebKitWebView *webkitwebview, gpointer ud);
+
+void
+destroy_cb (GtkWidget* widget, gpointer data);
+
+gboolean
+configure_event_cb(GtkWidget* window, GdkEventConfigure* event);
+
+gboolean
+key_press_cb (GtkWidget* window, GdkEventKey* event);
+
+gboolean
+key_release_cb (GtkWidget* window, GdkEventKey* event);
+
+gboolean
+navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame,
+ WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action,
+ WebKitWebPolicyDecision *policy_decision, gpointer user_data);
+
+gboolean
+new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame,
+ WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action,
+ WebKitWebPolicyDecision *policy_decision, gpointer user_data);
+
+gboolean
+mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request,
+ gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data);
+
+/*@null@*/ WebKitWebView*
+create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data);
+
+gboolean
+download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data);
+
diff --git a/docs/INSTALL b/docs/INSTALL
index 59bdaad..93e55f7 100644
--- a/docs/INSTALL
+++ b/docs/INSTALL
@@ -28,7 +28,7 @@ Though you can only get tagged versions from the master branch, which may be old
Dependencies
------------
* git (for downloading)
-* pkgconfig (for Make/gcc)
+* pkg-config (for Make/gcc)
* libwebkit 1.1.4 or higher
* libsoup 2.24 or higher (dep for webkit/gtk+)
* gtk 2.14 or higher
diff --git a/docs/TODO b/docs/TODO
index 45f4539..fa68a8b 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -1,3 +1,97 @@
+what if fifo/socket doesn't exist, or exists but nothing working behind it (anymore)?
+-> useful for both cookie handler and event manager
+-> implementing in uzbl-core needs refactoring of handler args, or some edge-case workarounds
+ (mason-l's proposal was not too bad: basically make cookie stuff more cookie specific.
+ we don't need a generic talk_to_socket. 3 vars: cookie_handler, cookie_handler_socket and
+ cookie_handler_launcher. act depending on which vars are set)
+-> for now, assume that we don't need to check for crashes, just make sure there is the initial socket/fifo -> can be done in uzbl-browser script
+
+
+== event-messages specific ==
+* throw out all old code
+* document the event handling mechanism, all events, how to get started with sample event handler
+* VARIABLE_SET for all types (but probably not useful for custom vars)
+* port keycmd to evt handler. we can now more cleanly send BackSpace instead of keycmd_bs and so on
+* port the concept of modes and all related variables
+* many uzbl-core's for 1 event manager. needs support in event_manager and uzbl-browser
+* event manager dynamic config system that configures itself based on events from uzbl (eg to set vars in "uzbl slaves" such as this, set custom vars in uzbl which we react to)
+* remove chain command
+* scalability -> 1 event manager per n uzbl-browser instances -> mkfifo and redirect uzbl-core stdout to fifo in uzbl-browser
+* event manager -> everything based on per instance basis (associative array with uzbl instance name as key)
+* migrate progress(bar) related code
+* migrate all *_handler to EM plugins
+* cookies over EM (but we can delay that. it works okay for now)
+
+
+= keybinding features =
+* use a datadriven config format to allow simple implementations in different languages.
+* allow binding all 'normal' keys ('a', 'Z', '_', '1', '@', '{', ...)
+* allow binding all 'special' keys and modkeys ('Space', 'BackSpace', 'Alt_L', 'Escape')
+* we could receive anything from /usr/include/X11/keysymdef.h (minus the 'XK_')
+* support keypresses and keyreleases
+* maybe: finegrained definitions of when releases can happen during a bigger command. this may be an overcomplication (YAGNI?)
+* maybe: after triggering, support resetting keycmd not to '', but something else. eg after 'gbbs' has triggered, set keycmd to 'bbs search' or something
+ thoughts: do we send fake key events to uzbl (xdotool?) or more a way to set the keycmd inside the event handler?
+ maybe not use a command but a static definition in the bind configs
+ it may be cumbersome to support special chars here, so lets not overcomplicate this
+* since we use key_press and release events, we don't need to support modmasks, we can "know" the state of any key, even non-modifier keys can be used as "fake" modkeys!
+* port old behaviors:
+ * `<string>` ends with underscore: the command will only be invoked after pressing return/enter, `%s` in the `<command>` string will be replaced by this text. (optional) (old `bind o _ = uri %s`)
+ -> we can now use Literal 'Enter' keysym. we could have the convention that any bind who ends with 'Enter' which gets extra text, will do the replacement thing.
+ * `<string>` ends with an asterisk: similar behavior as with an underscore, but also makes the binding incremental (i.e. the command will be invoked on every keystroke). (old `bind /* = search %s` )
+ -> we'll come up with a special marker. [!Enter] or whatever.
+ * `<string>` ends on a different character: you need to type the full string, which will trigger the command immediately, without pressing enter/return. (old `bind ZZ = exit`)
+ -> this should be the default.
+
+== proposed workflow ==
+in event_handler.py:
+ * incoming key.length > 1 (this should separate all special cases like F11, Alt_L etc from normal chars) -> put '[' and ']' around it
+ this allows to separate [F11] from a sequence of literal 'F', '1' '1'.
+ * to bind literal [, it should be escapable with '\' (which itself is also escapable. eg '\\' for literal '\')
+ * In most implementations, you'll want to:
+ if [BackSpace] && keycmd == '' -> go back or whatever. otherwise remove last char from keycmd
+ * this gives us the ' ' character for free. what to do with it?
+ ideas:
+ * use it as token for key_release event: 'z ' would mean: bind something on release of 'z', but we could also use a special marker like [release:<char>] for this (where <char> itself can also contain [] )
+ * use it a separator for "combo's" (like xbindkeys does)
+
+= proposed implementations =
+option1: always assume combo's (simultaneous keypresses)
+' ' -> combo separator
+'a ' -> bind something to key_release of 'a'
+
+abc -> press a b c at same time
+a b c -> press string 'abc'
+
+option2: combo's are explicit (dieters preference):
+'+' -> define combo (make + escapable)
+'a ' -> key_release a
+
+'a+b+c' -> press a b c at same time.
+'abc' -> string 'abc'
+'a+b+cfoo' -> a b c at same time, then 'foo'
+'a+(bc)' -> 'bc' with 'a' as fake modkey (make '()' escapable)
+'a+b a+c' -> same (though you could temporarily release a between pressing 'b' and 'c' if you want)
+'[Space]+foo' -> press space while typing 'f', then type 'oo' (make '[]' escapable)
+'[Alt_L]+[Backspace]' -> press alt while hitting backspace
+'[Alt_L][Backspace]' -> hit alt, then backspace
+'[Alt_L][Backspace] ' -> hit alt, then backspace then release backspace
+'z' and 'z ' -> bind things on press and release of 'z'
+
+= key handling (example event_handler.py) examples to implement =
+* on escape:
+if insert mode: set_insert_mode(uzbl.behave.always_insert_mode); update_title
+else: clear_keycmd(); update_title; dehilight(uzbl.gui.web_view, NULL, NULL);
+* demonstrate separate key_press and key_release (eg press 'z' to zoom in, on release reset zoom to what it was before. use "locking boolean" as discussed on irc)
+* port the Modkey concept
+* BackSpace -> keycmd_bs
+
+* Insert: insert from clipboard -> keycmd + update_title
+* shift+Insert: insert from primary -> keycmd + update_title
+* handle Return and/or KP_Enter
+
+
+
More or less in order of importance/urgency
* improve cookie handler.
@@ -6,7 +100,7 @@ More or less in order of importance/urgency
* store uri/tag/name/state in xorg window properties
* split up uzbl.c into multiple files
* shortcuts to focus other instances (see docs/multiple-instances-management)
-
+* cookie daemon (and maybe other daemons) are not scoped to the testing session when running some test-*-browser targets
* recognize -h with GOption?
* implement getting feedback from socket
* scrolling: make page up and page down configurable.
diff --git a/events.c b/events.c
new file mode 100644
index 0000000..abd794b
--- /dev/null
+++ b/events.c
@@ -0,0 +1,164 @@
+/*
+ ** Uzbl event routines
+ ** (c) 2009 by Robert Manea
+*/
+
+#include "uzbl-core.h"
+#include "events.h"
+
+UzblCore uzbl;
+
+/* Event id to name mapping
+ * 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" ,
+ "KEY_PRESS" ,
+ "KEY_RELEASE" ,
+ "DOWNLOAD_REQUEST" ,
+ "COMMAND_EXECUTED" ,
+ "LINK_HOVER" ,
+ "TITLE_CHANGED" ,
+ "GEOMETRY_CHANGED" ,
+ "WEBINSPECTOR" ,
+ "NEW_WINDOW" ,
+ "SELECTION_CHANGED",
+ "VARIABLE_SET" ,
+ "FIFO_SET" ,
+ "SOCKET_SET" ,
+ "INSTANCE_START" ,
+ "INSTANCE_EXIT" ,
+ "LOAD_PROGRESS" ,
+ "LINK_UNHOVER"
+};
+
+void
+event_buffer_timeout(guint sec) {
+ struct itimerval t;
+ memset(&t, 0, sizeof t);
+ t.it_value.tv_sec = sec;
+ t.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &t, NULL);
+}
+
+
+void
+send_event_socket(GString *msg) {
+ GError *error = NULL;
+ GString *tmp;
+ GIOStatus ret = 0;
+ gsize len;
+ guint i=0;
+
+ if(uzbl.comm.socket_path &&
+ uzbl.comm.clientchan &&
+ uzbl.comm.clientchan->is_writeable) {
+
+ if(uzbl.state.event_buffer) {
+ event_buffer_timeout(0);
+
+ while(i < uzbl.state.event_buffer->len) {
+ tmp = g_ptr_array_index(uzbl.state.event_buffer, i++);
+ ret = g_io_channel_write_chars (uzbl.comm.clientchan,
+ tmp->str, tmp->len,
+ &len, &error);
+ /* is g_ptr_array_free(uzbl.state.event_buffer, TRUE) enough? */
+ //g_string_free(tmp, TRUE);
+ if (ret == G_IO_STATUS_ERROR) {
+ g_warning ("Error sending event to socket: %s", error->message);
+ }
+ g_io_channel_flush(uzbl.comm.clientchan, &error);
+ }
+ g_ptr_array_free(uzbl.state.event_buffer, TRUE);
+ uzbl.state.event_buffer = NULL;
+ }
+ if(msg) {
+ while(!ret ||
+ ret == G_IO_STATUS_AGAIN) {
+ ret = g_io_channel_write_chars (uzbl.comm.clientchan,
+ msg->str, msg->len,
+ &len, &error);
+ }
+
+ if (ret == G_IO_STATUS_ERROR) {
+ g_warning ("Error sending event to socket: %s", error->message);
+ }
+ g_io_channel_flush(uzbl.comm.clientchan, &error);
+ }
+ }
+ /* buffer events until a socket is set and connected
+ * or a timeout is encountered
+ */
+ else {
+ if(!uzbl.state.event_buffer)
+ uzbl.state.event_buffer = g_ptr_array_new();
+ g_ptr_array_add(uzbl.state.event_buffer, (gpointer)g_string_new(msg->str));
+ }
+}
+
+void
+send_event_stdout(GString *msg) {
+ printf("%s", msg->str);
+ fflush(stdout);
+}
+
+/*
+ * build event string and send over the supported interfaces
+ * custom_event == NULL indicates an internal event
+*/
+void
+send_event(int type, const gchar *details, const gchar *custom_event) {
+ GString *event_message = g_string_new("");
+ gchar *buf, *p_val = NULL;
+
+ /* expand shell vars */
+ if(details) {
+ buf = g_strdup(details);
+ p_val = parseenv(g_strdup(buf ? g_strchug(buf) : " "));
+ g_free(buf);
+ }
+
+ /* check for custom events */
+ if(custom_event) {
+ g_string_printf(event_message, "EVENT [%s] %s %s\n",
+ uzbl.state.instance_name, custom_event, p_val);
+ }
+ /* check wether we support the internal event */
+ else if(type < LAST_EVENT) {
+ g_string_printf(event_message, "EVENT [%s] %s %s\n",
+ uzbl.state.instance_name, event_table[type], p_val);
+ }
+
+ if(event_message->str) {
+ /* TODO: a means to select the interface to which events are sent */
+ send_event_stdout(event_message);
+ send_event_socket(event_message);
+
+ g_string_free(event_message, TRUE);
+ }
+ g_free(p_val);
+}
+
+/* Transform gdk key events to our own events */
+void
+key_to_event(guint keyval, gint mode) {
+ char byte[2];
+
+ /* check for Latin-1 characters (1:1 mapping) */
+ if ((keyval > 0x0020 && keyval <= 0x007e) ||
+ (keyval >= 0x00a0 && keyval <= 0x00ff)) {
+ sprintf(byte, "%c", keyval);
+ send_event(mode == GDK_KEY_PRESS ? KEY_PRESS : KEY_RELEASE,
+ byte, NULL);
+ }
+ else
+ send_event(mode == GDK_KEY_PRESS ? KEY_PRESS : KEY_RELEASE,
+ gdk_keyval_name(keyval), NULL);
+
+}
diff --git a/events.h b/events.h
new file mode 100644
index 0000000..15e33e6
--- /dev/null
+++ b/events.h
@@ -0,0 +1,33 @@
+/*
+ ** Uzbl event routines
+ ** (c) 2009 by Robert Manea
+*/
+
+/* Event system */
+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, NEW_WINDOW, SELECTION_CHANGED,
+ VARIABLE_SET, FIFO_SET, SOCKET_SET,
+ INSTANCE_START, INSTANCE_EXIT, LOAD_PROGRESS,
+ LINK_UNHOVER,
+
+ /* must be last entry */
+ LAST_EVENT
+};
+
+void
+event_buffer_timeout(guint sec);
+
+void
+send_event_socket(GString *msg);
+
+void
+send_event_stdout(GString *msg);
+
+void
+send_event(int type, const gchar *details, const gchar *custom_event);
+
+void
+key_to_event(guint keyval, gint mode);
diff --git a/examples/config/uzbl/config b/examples/config/uzbl/config
index 47d8839..e002178 100644
--- a/examples/config/uzbl/config
+++ b/examples/config/uzbl/config
@@ -1,88 +1,144 @@
# example uzbl config.
# all settings are optional. you can use uzbl without any config at all (but it won't do much)
-# keyboard behavior in this sample config is sort of vimstyle
+# === Shortcuts ==============================================================
-# Handlers
-set download_handler = spawn $XDG_DATA_HOME/uzbl/scripts/download.sh
-set cookie_handler = spawn $XDG_DATA_HOME/uzbl/scripts/cookies.py
-#set new_window = sh 'echo uri "$8" > $4' # open in same window
-set new_window = sh 'uzbl -u $8' # equivalent to the default behaviour
-set scheme_handler = spawn $XDG_DATA_HOME/uzbl/scripts/scheme.py
-set load_start_handler = chain 'set keycmd = ' 'set status_message = <span foreground="khaki">wait</span>'
-set load_commit_handler = set status_message = <span foreground="green">recv</span>
-set load_finish_handler = chain 'set status_message = <span foreground="gold">done</span>' 'spawn $XDG_DATA_HOME/uzbl/scripts/history.sh'
+# request BIND <keycmd> = <command>
+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
+# request ON_EVENT <EVENT_NAME> <command>
+set on_event = request ON_EVENT
+# request PROGRESS_CONFIG <key> = <value>
+set progress = request PROGRESS_CONFIG
+set set_mode = set mode =
+set set_status = set status_message =
+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:/usr/local/share/uzbl/examples/data/uzbl:scripts
+
+
+# === Handlers ===============================================================
+
+set download_handler = spawn @scripts_dir/download.sh
+set cookie_handler = talk_to_socket $XDG_CACHE_HOME/uzbl/cookie_daemon_socket
+set scheme_handler = spawn @scripts_dir/scheme.py
+
+# New window handler options
+#set new_window = sh 'echo uri "$8" > $4' # open in same window
+set new_window = sh 'uzbl-browser -u $8' # equivalent to the default behaviour
+
+# Load start handler
+@on_event LOAD_START @set_status <span foreground="khaki">wait</span>
+
+# Load commit handler
+@on_event LOAD_COMMIT @set_status <span foreground="green">recv</span>
+
+# Load finish handlers
+@on_event LOAD_FINISH @set_status <span foreground="gold">done</span>
+@on_event LOAD_FINISH spawn @scripts_dir/history.sh
+
+# Misc on_event handlers
+#@on_event CONFIG_CHANGED print Config changed: %1 = %2
+
+
+# === Behaviour and appearance ===============================================
-# Behaviour and appearance
set show_status = 1
-set status_background = #303030
-set status_format = <span font_family="monospace"><span background="khaki" foreground="black">[\@[\@MODE]\@]</span> [<span weight="bold" foreground="red">\@[\@keycmd]\@</span>] <span foreground="#606060"> \@[\@LOAD_PROGRESSBAR]\@ </span><span foreground="#99FF66">\@[\@uri]\@</span> <span foreground="khaki">\@[\@NAME]\@</span> <span foreground="orange">\@status_message</span><span foreground="#606060"> \@[\@SELECTED_URI]\@</span></span>
set status_top = 0
-set insert_indicator = I
-set command_indicator = C
-set useragent = Uzbl (Webkit @WEBKIT_MAJOR.@WEBKIT_MINOR.@WEBKIT_MICRO) (@(uname -o)@ @(uname -m)@ [@ARCH_UZBL]) (Commit @COMMIT)
-
-set fifo_dir = /tmp
-set socket_dir = /tmp
-set shell_cmd = sh -c
-
-# Keyboard interface
-set modkey = Mod1
-# like this you can enter any command at runtime, interactively. prefixed by ':'
-bind :_ = chain '%s'
-
-bind j = scroll_vert 20
-bind k = scroll_vert -20
-bind h = scroll_horz -20
-bind l = scroll_horz 20
-bind << = scroll_begin
-bind >> = scroll_end
-bind b = back
-bind m = forward
-bind S = stop
-bind r = reload
-bind R = reload_ign_cache
-bind + = zoom_in
-bind - = zoom_out
-bind T = toggle_zoom_type
-bind 1 = sh "echo set zoom_level = 1.0 > $4"
-bind 2 = sh "echo set zoom_level = 2.0 > $4"
-bind t = toggle_status
-bind /* = search %s
-bind ?* = search_reverse %s
+set status_background = #303030
+
+set keycmd_style = weight="bold" 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 progress_section = <span foreground="#606060">\@[\@progress_format]\@</span>
+set uri_section = <span foreground="#99FF66">\@[\@uri]\@</span>
+set name_section = <span foreground="khaki">\@[\@NAME]\@</span>
+set status_section = <span foreground="orange">\@status_message</span>
+set selected_section = <span foreground="#606060">\@[\@SELECTED_URI]\@</span>
+
+set status_format = <span font_family="monospace">@mode_section @keycmd_section @progress_section @uri_section @name_section @status_section @selected_section</span>
+
+# Progress bar config
+@progress width = 8
+# %d = done, %p = pending %c = percent done, %i = int done, %s = spinner,
+# %t = percent pending, %o = int pending, %r = sprite scroll
+@progress format = [%d>%p]%c
+@progress done = =
+@progress pending =
+
+# Or ride those spinnas'
+#@progress format = [%d%s%p]
+#@progress spinner = -\\|/
+#@progress done = -
+#@progress pending =
+
+
+# === Core settings ==========================================================
+
+set useragent = Uzbl (Webkit @WEBKIT_MAJOR.@WEBKIT_MINOR.@WEBKIT_MICRO) (@(uname -o)@ @(uname -m)@ [@ARCH_UZBL]) (Commit @COMMIT)
+set fifo_dir = /tmp
+set socket_dir = /tmp
+
+
+# === Keyboard bindings ======================================================
+
+# With this command you can enter in any command at runtime when prefixed with
+# a colon.
+@bind :_ = chain '%s'
+
+@bind j = scroll_vert 20
+@bind k = scroll_vert -20
+@bind h = scroll_horz -20
+@bind l = scroll_horz 20
+@bind << = scroll_begin
+@bind >> = scroll_end
+@bind b = back
+@bind m = forward
+@bind S = stop
+@bind r = reload
+@bind R = reload_ign_cache
+@bind + = zoom_in
+@bind - = zoom_out
+@bind T = toggle_zoom_type
+@bind 1 = sh "echo set zoom_level = 1.0 > $4"
+@bind 2 = sh "echo set zoom_level = 2.0 > $4"
+@bind t = toggle_status
+@bind /* = search %s
+@bind ?* = search_reverse %s
#jump to next
-bind n = search
-bind N = search_reverse
-bind gh = uri http://www.uzbl.org
-# shortcut to set the uri. TODO: i think we can abandon the uri command in favor of 'set uri = ..'
-bind o _ = uri %s
+@bind n = search
+@bind N = search_reverse
+@bind gh = uri http://www.uzbl.org
# shortcut to set variables
-bind s _ = set %s
-bind \wiki _ = uri http://wiki.archlinux.org/index.php/Special:Search?search=%s&go=Go
-bind gg _ = uri http://www.google.com/search?q=%s
-bind i = toggle_insert_mode
-# disable insert mode (1 to enable). note that Esc works to disable, regardless of this setting
-bind I = toggle_insert_mode 0
+@bind s _ = set %s
+@bind \wiki _ = uri http://wiki.archlinux.org/index.php/Special:Search?search=%s&go=Go
+@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 $XDG_DATA_HOME/uzbl/scripts/insert_bookmark.sh
-bind U = spawn $XDG_DATA_HOME/uzbl/scripts/load_url_from_history.sh
-bind u = spawn $XDG_DATA_HOME/uzbl/scripts/load_url_from_bookmarks.sh
+#@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
-bind yurl = spawn $XDG_DATA_HOME/uzbl/scripts/yank.sh 6 primary
-bind ytitle = spawn $XDG_DATA_HOME/uzbl/scripts/yank.sh 7 clipboard
+@bind yurl = spawn @scripts_dir/yank.sh 6 primary
+@bind ytitle = spawn @scripts_dir/yank.sh 7 clipboard
# does the same as yurl but without needing a script
-bind y2url = sh 'echo -n $6 | xclip'
+@bind y2url = sh 'echo -n $6 | xclip'
# go the page from primary selection
-bind p = sh 'echo "uri `xclip -selection primary -o`" > $4'
+@bind p = sh 'echo "uri `xclip -selection primary -o`" > $4'
# go to the page in clipboard
-bind P = sh 'echo "uri `xclip -selection clipboard -o`" > $4'
+@bind P = sh 'echo "uri `xclip -selection clipboard -o`" > $4'
# start a new uzbl instance from the page in primary selection
-bind 'p = sh 'exec uzbl --uri $(xclip -o)'
-bind ZZ = exit
-bind Xs = js alert("hi");
+@bind 'p = sh 'exec uzbl --uri $(xclip -o)'
+@bind ZZ = exit
+@bind Xs = js alert("hi");
# example showing how to use sh
# it sends a command to the fifo, whose path is told via a positional param
# if fifo_dir is not set, it'll echo to a file named (null) somewhere >:) remember to delete it
@@ -90,32 +146,84 @@ bind Xs = js alert("hi");
# you must enclose it in quotes. Remember to escape (and double-escape) quotes and backslashes
# in the body. Any additional parameters you use will appear AFTER the default parameters (cfg file
# path, fifo & socket dirs, etc.)
-bind XS = sh 'echo "js alert (\\"This is sent by the shell via a fifo\\")" > "$4"'
+@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 !dump = sh "echo dump_config > $4"
+@bind !reload = sh 'cat $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
-bind za = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh
-bind ze = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh edit
-bind zn = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh new
-bind zl = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh load
+set formfiller = spawn @scripts_dir/formfiller
+@bind za = @{formfiller}.sh
+@bind ze = @{formfiller}.sh edit
+@bind zn = @{formfiller}.sh new
+@bind zl = @{formfiller}.sh load
-# other - more advanced - implementation using perl: (could not get this to run - Dieter )
-bind LL = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.pl load
-bind LN = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.pl new
-bind LE = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.pl edit
+# Or the more advanced implementation using perl: (could not get this to run - Dieter)
+@bind LL = @{formfiller}.pl load
+@bind LN = @{formfiller}.pl new
+@bind LE = @{formfiller}.pl edit
# we ship some javascripts to do keyboard based link hinting/following. (webkit does not have C DOM bindings yet)
# this is similar to how it works in vimperator (and konqueror)
# TODO: did we resolve: "no click() event for hyperlinks so no referrer set" ?
#hit F to toggle the Hints (now in form of link numbering)
-bind F = script $XDG_DATA_HOME/uzbl/scripts/hint.js
+@bind F = script @scripts_dir/hint.js
# the most stable version:
-bind fl* = script $XDG_DATA_HOME/uzbl/scripts/follow_Numbers.js %s
+@bind fl* = script @scripts_dir/follow_Numbers.js %s
# using strings, not polished yet:
-bind fL* = script $XDG_DATA_HOME/uzbl/scripts/follow_Numbers_Strings.js %s
+@bind fL* = script @scripts_dir/follow_Numbers_Strings.js %s
+
+
+# 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 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/
+
+
+# === Mode configuration =====================================================
+
+# Define some mode specific uzbl configurations.
+set command = @mode_config command
+set insert = @mode_config insert
+set stack = @mode_config stack
+
+# Command mode config.
+@command keycmd_style = weight="bold" foreground="red"
+@command status_background = #202020
+@command mode_indicator = Cmd
+
+# Insert mode config.
+@insert status_background = #303030
+@insert mode_indicator = Ins
+
+# Multi-stage-binding mode config.
+@stack keycmd_events = 1
+@stack modcmd_updates = 1
+@stack keycmd_style = foreground="red"
+@stack prompt_style = foreground="#888" weight="light"
+@stack status_background = #202020
+@stack mode_indicator = Bnd
+
+set default_mode = command
+
+# Mode bindings:
+# Changing mode method via set.
+@bind I = @set_mode insert
+
+# Or toggle between modes by rasing request events.
+set toggle_cmd_ins = @toggle_modes command insert
+
+@bind i = @toggle_cmd_ins
+@bind <Ctrl>i = @toggle_cmd_ins
+
+# === Post-load misc commands ===============================================
-# "home" page if you will
+# Set the "home" page.
set uri = uzbl.org
diff --git a/examples/data/uzbl/scripts/event_manager.py b/examples/data/uzbl/scripts/event_manager.py
new file mode 100755
index 0000000..2e84ded
--- /dev/null
+++ b/examples/data/uzbl/scripts/event_manager.py
@@ -0,0 +1,728 @@
+#!/usr/bin/env python
+
+# Event Manager for Uzbl
+# Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com>
+# Copyright (c) 2009, Dieter Plaetinck <dieter@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
+# the Free Software Foundation, either version 3 of the License, or
+# (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
+# 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/>.
+
+'''
+
+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
+====
+
+ - 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 imp
+import os
+import sys
+import select
+import re
+import types
+import socket
+import pprint
+import time
+from optparse import OptionParser
+from traceback import print_exc
+
+
+# ============================================================================
+# ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
+# ============================================================================
+
+
+def xdghome(key, default):
+ '''Attempts to use the environ XDG_*_HOME paths if they exist otherwise
+ use $HOME and the default path.'''
+
+ xdgkey = "XDG_%s_HOME" % key
+ if xdgkey in os.environ.keys() and os.environ[xdgkey]:
+ return os.environ[xdgkey]
+
+ 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': False,
+ 'plugin_dir': "$XDG_DATA_HOME/uzbl/scripts/plugins/",
+ 'plugins_load': [],
+ 'plugins_ignore': [],
+}
+
+
+# ============================================================================
+# ::: End of configuration section :::::::::::::::::::::::::::::::::::::::::::
+# ============================================================================
+
+
+# Define some globals.
+_SCRIPTNAME = os.path.basename(sys.argv[0])
+_RE_FINDSPACES = re.compile("\s+")
+
+def echo(msg):
+ '''Prints only if the verbose flag has been set.'''
+
+ 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))
+
+
+def counter():
+ '''Generate unique object id's.'''
+
+ i = 0
+ while True:
+ i += 1
+ 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__")
+
+
+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_all_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, name, 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." % name), allmodules)
+
+ for ref in allrefs:
+ del sys.modules[ref]
+
+ if name in sys.modules.keys():
+ del sys.modules[name]
+
+ if name in self:
+ del self[name]
+
+ if remove_pyc:
+ pyc = os.path.join(self.plugin_dir, '%s.pyc' % name)
+ if os.path.exists(pyc):
+ os.remove(pyc)
+
+
+ def load_plugins(self):
+
+ if config['plugins_load']:
+ pluginlist = config['plugins_load']
+
+ else:
+ pluginlist = self._find_all_plugins()
+ for name in config['plugins_ignore']:
+ if name in pluginlist:
+ pluginlist.remove(name)
+
+ for name in pluginlist:
+ # Make sure the plugin isn't already loaded.
+ self._unload_plugin(name)
+
+ try:
+ moduleinfo = imp.find_module(name, [self.plugin_dir,])
+ plugin = imp.load_module(name, *moduleinfo)
+ self[name] = plugin
+
+ except:
+ raise
+
+ if 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()
+
+
+class CallPrepender(object):
+ '''Execution argument modifier. Takes (arg, function) then modifies the
+ function call:
+
+ -> function(*args, **kargs) -> function(arg, *args, **kargs) ->'''
+
+ def __init__(self, uzbl, function):
+ self.function = function
+ self.uzbl = uzbl
+
+ def call(self, *args, **kargs):
+ return self.function(self.uzbl, *args, **kargs)
+
+
+class Handler(object):
+
+ nexthid = counter().next
+
+ def __init__(self, event, handler, *args, **kargs):
+ self.callable = iscallable(handler)
+ if self.callable:
+ self.function = handler
+ self.args = args
+ self.kargs = kargs
+
+ elif kargs:
+ raise ArgumentError("cannot supply kargs with a uzbl command")
+
+ elif isiterable(handler):
+ self.commands = handler
+
+ else:
+ self.commands = [handler,] + list(args)
+
+ self.event = event
+ self.hid = self.nexthid()
+
+
+ def __repr__(self):
+ args = ["event=%s" % self.event, "hid=%d" % self.hid]
+
+ if self.callable:
+ args.append("function=%r" % self.function)
+ if self.args:
+ args.append("args=%r" % self.args)
+
+ if self.kargs:
+ args.append("kargs=%r" % self.kargs)
+
+ else:
+ cmdlen = len(self.commands)
+ cmds = self.commands[0] if cmdlen == 1 else self.commands
+ args.append("command%s=%r" % ("s" if cmdlen-1 else "", cmds))
+
+ return "<EventHandler(%s)>" % ', '.join(args)
+
+
+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 = {}
+ self._running = None
+ self._buffer = ''
+
+ self._handlers = {}
+
+ # 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
+
+ if not self.plugins:
+ self.plugins = PluginManager()
+
+ # Call the init() function in every plugin which then setup their
+ # respective hooks (event handlers, binds or timers).
+ self._init_plugins()
+
+
+ def __getattribute__(self, name):
+ '''Expose any exported functions before class functions.'''
+
+ if not name.startswith('_'):
+ exports = object.__getattribute__(self, '_exports')
+ if name in exports:
+ return exports[name]
+
+ return object.__getattribute__(self, name)
+
+
+ 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():
+ if not hasattr(plugin, '__export__'):
+ continue
+
+ for export in plugin.__export__:
+ if export in self._exports:
+ orig = self._exports[export]
+ raise KeyError("already exported attribute: %r" % export)
+
+ obj = getattr(plugin, export)
+ if iscallable(obj):
+ # Wrap the function in the CallPrepender object to make
+ # the exposed functions act like instance methods.
+ obj = CallPrepender(self, obj).call
+
+ self._exports[export] = 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()
+ raise
+
+
+ def _init_uzbl_socket(self, uzbl_socket=None, timeout=None):
+ '''Store socket location and open socket connection to uzbl socket.'''
+
+ if uzbl_socket is None:
+ uzbl_socket = self.uzbl_socket
+
+ if not uzbl_socket:
+ error("no socket location.")
+ return
+
+ if not os.path.exists(uzbl_socket):
+ if timeout is None:
+ error("uzbl socket doesn't exist: %r" % uzbl_socket)
+ return
+
+ waitlimit = time.time() + timeout
+ echo("waiting for uzbl socket: %r" % uzbl_socket)
+ while not os.path.exists(uzbl_socket):
+ time.sleep(0.25)
+ if time.time() > waitlimit:
+ error("timed out waiting for socket: %r" % uzbl_socket)
+ return
+
+ self.uzbl_socket = uzbl_socket
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.uzbl_socket)
+ self._socket = sock
+
+
+ def _close_socket(self):
+ '''Close the socket used for communication with the uzbl instance.
+ This function is normally called upon receiving the INSTANCE_EXIT
+ event.'''
+
+ if self._socket:
+ self._socket.close()
+
+ self.uzbl_socket = self._socket = None
+
+
+ def _flush(self):
+ '''Flush messages from the outgoing queue to the uzbl instance.'''
+
+ 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 '<-- %s' % msg
+ h.write(("%s\n" % msg).encode('utf-8'))
+
+ h.close()
+
+ if len(self._socket_cmd_queue) and self.uzbl_socket:
+ if not self._socket and os.path.exists(self.uzbl_socket):
+ self._init_uzbl_socket()
+
+ if self._socket:
+ while len(self._socket_cmd_queue):
+ msg = self._socket_cmd_queue.pop(0)
+ print '<-- %s' % msg
+ self._socket.send(("%s\n" % msg).encode('utf-8'))
+
+
+ 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 the newly created handler.
+ Handlers can either be a function or a uzbl command string.'''
+
+ if event not in self._handlers.keys():
+ self._handlers[event] = []
+
+ handler = Handler(event, handler, *args, **kargs)
+ self._handlers[event].append(handler)
+
+ print handler
+ return handler
+
+
+ def connect_dict(self, connect_dict):
+ '''Connect a dictionary comprising of {"EVENT_NAME": handler, ..} to
+ the event handler stack.
+
+ If you need to supply args or kargs to an event use the normal connect
+ function.'''
+
+ for (event, handler) in connect_dict.items():
+ self.connect(event, handler)
+
+
+ def remove_by_id(self, hid):
+ '''Remove connected event handler by unique handler id.'''
+
+ for (event, handlers) in self._handlers.items():
+ for handler in list(handlers):
+ if hid != handler.hid:
+ continue
+
+ echo("removed %r" % handler)
+ handlers.remove(handler)
+ return
+
+ echo('unable to find & remove handler with id: %d' % handler.hid)
+
+
+ def remove(self, handler):
+ '''Remove connected event handler.'''
+
+ for (event, handlers) in self._handlers.items():
+ if handler in handlers:
+ echo("removed %r" % handler)
+ handlers.remove(handler)
+ return
+
+ echo('unable to find & remove handler: %r' % handler)
+
+
+ def listen_from_fd(self, fd):
+ '''Polls for event messages from fd.'''
+
+ try:
+ self._running = True
+ while self._running:
+ if select.select([fd,], [], [], 1)[0]:
+ self.read_from_fd(fd)
+ continue
+
+ self._flush()
+
+ except KeyboardInterrupt:
+ print
+
+ except:
+ #print_exc()
+ raise
+
+
+ def read_from_fd(self, fd):
+ '''Reads event messages from a single 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
+ print "---", raw.rstrip()
+ return
+
+ event, args = msg[1], msg[3]
+ self.handle_event(event, args)
+
+
+ def listen_from_uzbl_socket(self, uzbl_socket):
+ '''Polls for event messages from a single uzbl socket.'''
+
+ self._init_uzbl_socket(uzbl_socket, 10)
+
+ if not self._socket:
+ error("failed to init socket: %r" % uzbl_socket)
+ return
+
+ self._flush()
+ try:
+ self._running = True
+ while self._running:
+ if select.select([self._socket], [], [], 1):
+ self.read_from_uzbl_socket()
+ continue
+
+ self._flush()
+
+ except KeyboardInterrupt:
+ print
+
+ except:
+ #print_exc()
+ raise
+
+
+ def read_from_uzbl_socket(self):
+ '''Reads event messages from a uzbl socket.'''
+
+ raw = unicode(self._socket.recv(8192), 'utf-8', 'ignore')
+ if not raw:
+ # Read null byte
+ self._running = False
+ return
+
+ self._buffer += raw
+ msgs = self._buffer.split("\n")
+ self._buffer = msgs.pop()
+
+ for msg in msgs:
+ msg = msg.rstrip()
+ if not msg:
+ continue
+
+ cmd = _RE_FINDSPACES.split(msg, 3)
+ if not cmd or cmd[0] != "EVENT":
+ # Not an event message
+ print msg.rstrip()
+ continue
+
+ if len(cmd) < 4:
+ cmd.append('')
+
+ event, args = cmd[2], cmd[3]
+ try:
+ self.handle_event(event, args)
+
+ except:
+ #print_exc()
+ raise
+
+
+ def handle_event(self, event, args):
+ '''Handle uzbl events internally before dispatch.'''
+
+ if event == 'FIFO_SET':
+ self.uzbl_fifo = args
+ self._flush()
+
+ elif event == 'SOCKET_SET':
+ if not self.uzbl_socket or not self._socket:
+ self._init_uzbl_socket(args)
+ self._flush()
+
+ elif event == 'INSTANCE_EXIT':
+ self._close_socket()
+ self._running = False
+ for (name, plugin) in self.plugins.items():
+ if hasattr(plugin, "cleanup"):
+ plugin.cleanup(uzbl)
+
+ # Now handle the event "publically".
+ self.event(event, args)
+
+
+ def exec_handler(self, handler, *args, **kargs):
+ '''Execute either the handler function or send the handlers uzbl
+ commands via the socket.'''
+
+ if handler.callable:
+ args = args + handler.args
+ kargs = dict(handler.kargs.items()+kargs.items())
+ handler.function(uzbl, *args, **kargs)
+
+ else:
+ if kargs:
+ raise ArgumentError('cannot supply kargs for uzbl commands')
+
+ for command in handler.commands:
+ if '%s' in command:
+ if len(args) > 1:
+ for arg in args:
+ command = command.replace('%s', arg, 1)
+
+ elif len(args) == 1:
+ command = command.replace('%s', args[0])
+
+ uzbl.send(command)
+
+
+ def event(self, event, *args, **kargs):
+ '''Raise a custom event.'''
+
+ # Silence _printing_ of geo events while still debugging.
+ if event != "GEOMETRY_CHANGED":
+ print "--> %s %s %s" % (event, args, '' if not kargs else kargs)
+
+ if event in self._handlers:
+ for handler in self._handlers[event]:
+ try:
+ self.exec_handler(handler, *args, **kargs)
+
+ except:
+ #print_exc()
+ raise
+
+
+if __name__ == "__main__":
+ #uzbl = UzblInstance().listen_from_fd(sys.stdin)
+
+ parser = OptionParser()
+ parser.add_option('-s', '--uzbl-socket', dest='socket',
+ action="store", metavar="SOCKET",
+ help="read event messages from uzbl socket.")
+
+ parser.add_option('-v', '--verbose', dest='verbose', action="store_true",
+ help="print verbose output.")
+
+ parser.add_option('-d', '--plugin-dir', dest='plugin_dir', action="store",
+ metavar="DIR", help="change plugin directory.")
+
+ parser.add_option('-p', '--load-plugins', dest="load", action="store",
+ metavar="PLUGINS", help="comma separated list of plugins to load")
+
+ parser.add_option('-i', '--ignore-plugins', dest="ignore", action="store",
+ metavar="PLUGINS", help="comma separated list of plugins to ignore")
+
+ parser.add_option('-l', '--list-plugins', dest='list', action='store_true',
+ help="list all the plugins in the plugin dir.")
+
+ (options, args) = parser.parse_args()
+
+ if len(args):
+ for arg in args:
+ error("unknown argument: %r" % arg)
+
+ raise ArgumentError
+
+ if options.verbose:
+ config['verbose'] = True
+
+ if options.plugin_dir:
+ plugin_dir = os.path.expandvars(options.plugin_dir)
+ if not os.path.isdir(plugin_dir):
+ error("%r is not a directory" % plugin_dir)
+ sys.exit(1)
+
+ config['plugin_dir'] = plugin_dir
+ echo("changed plugin dir: %r" % plugin_dir)
+
+ 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())
+
+ echo('only loading plugin(s): %s' % ', '.join(plugins_load))
+
+ elif options.ignore:
+ plugins_ignore = config['plugins_ignore']
+ for plugin in options.ignore.split(','):
+ if plugin.strip():
+ plugins_ignore.append(plugin.strip())
+
+ echo('ignoring plugin(s): %s' % ', '.join(plugins_ignore))
+
+
+ if options.list:
+ plugin_dir = os.path.expandvars(config['plugin_dir'])
+ if not os.path.isdir(plugin_dir):
+ error("not a directory: %r" % plugin_dir)
+ sys.exit(1)
+
+ dirlist = filter(lambda p: p.endswith('.py'), os.listdir(plugin_dir))
+ print ', '.join([p[:-3] for p in dirlist])
+
+ else:
+ uzbl = UzblInstance()
+ if options.socket:
+ echo("listen from uzbl socket: %r" % options.socket)
+ uzbl.listen_from_uzbl_socket(options.socket)
+
+ else:
+ uzbl.listen_from_fd(sys.stdin)
diff --git a/examples/data/uzbl/scripts/instance-select-wmii.sh b/examples/data/uzbl/scripts/instance-select-wmii.sh
new file mode 100755
index 0000000..2bf13ba
--- /dev/null
+++ b/examples/data/uzbl/scripts/instance-select-wmii.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+
+# This script allows you to focus another uzbl window
+# It considers all uzbl windows in the current tag
+# you can select one from a list, or go to the next/previous one
+# It does not change the layout (stacked/tiled/floating) nor does it
+# changes the size or viewing mode of a uzbl window
+# When your current uzbl window is maximized, the one you change to
+# will be maximized as well.
+# See http://www.uzbl.org/wiki/wmii for more info
+# $1 must be one of 'list', 'next', 'prev'
+
+COLORS=" -nb #303030 -nf khaki -sb #CCFFAA -sf #303030"
+
+if dmenu --help 2>&1 | grep -q '\[-rs\] \[-ni\] \[-nl\] \[-xs\]'
+then
+ DMENU="dmenu -i -xs -rs -l 10" # vertical patch
+else
+ DMENU="dmenu -i"
+fi
+
+if [ "$1" == 'list' ]
+then
+ list=
+ # get window id's of uzbl clients. we could also get the label in one shot but it's pretty tricky
+ for i in $(wmiir read /tag/sel/index | grep uzbl |cut -d ' ' -f2)
+ do
+ label=$(wmiir read /client/$i/label)
+ list="$list$i : $label\n"
+ done
+ window=$(echo -e "$list" | $DMENU $COLORS | cut -d ' ' -f1)
+ wmiir xwrite /tag/sel/ctl "select client $window"
+elif [ "$1" == 'next' ]
+then
+ current=$(wmiir read /client/sel/ctl | head -n 1)
+ # find the next uzbl window and focus it
+ next=$(wmiir read /tag/sel/index | grep -A 10000 " $current " | grep -m 1 uzbl | cut -d ' ' -f2)
+ if [ x"$next" != "x" ]
+ then
+ wmiir xwrite /tag/sel/ctl "select client $next"
+ fi
+elif [ "$1" == 'prev' ]
+then
+ current=$(wmiir read /client/sel/ctl | head -n 1)
+ prev=$(wmiir read /tag/sel/index | grep -B 10000 " $current " | tac | grep -m 1 uzbl | cut -d ' ' -f2)
+ if [ x"$prev" != "x" ]
+ then
+ wmiir xwrite /tag/sel/ctl "select client $prev"
+ fi
+else
+ echo "\$1 not valid" >&2
+ exit 2
+fi
diff --git a/examples/data/uzbl/scripts/plugins/bind.py b/examples/data/uzbl/scripts/plugins/bind.py
new file mode 100644
index 0000000..15f6f8e
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/bind.py
@@ -0,0 +1,374 @@
+'''Plugin provides support for binds in uzbl.
+
+For example:
+ event BIND ZZ = exit -> bind('ZZ', 'exit')
+ event BIND o _ = uri %s -> bind('o _', 'uri %s')
+ event BIND fl* = sh 'echo %s' -> bind('fl*', "sh 'echo %s'")
+
+And it is also possible to execute a function on activation:
+ bind('DD', myhandler)
+'''
+
+import sys
+import re
+from event_manager import config, counter, iscallable, isiterable
+
+# Export these variables/functions to uzbl.<name>
+__export__ = ['bind', 'del_bind', 'del_bind_by_glob', 'get_binds']
+
+# Hold the bind lists per uzbl instance.
+UZBLS = {}
+
+# Commonly used regular expressions.
+starts_with_mod = re.compile('^<([A-Z][A-Za-z0-9-_]+)>')
+find_prompts = re.compile('<([^:>]*):>').split
+
+# For accessing a bind glob stack.
+MOD_CMD, ON_EXEC, HAS_ARGS, GLOB = range(4)
+
+
+class BindParseError(Exception):
+ pass
+
+
+def echo(msg):
+ if config['verbose']:
+ print 'bind plugin:', msg
+
+
+def error(msg):
+ sys.stderr.write('bind plugin: error: %s\n' % msg)
+
+
+def ismodbind(glob):
+ '''Return True if the glob specifies a modbind.'''
+
+ return bool(starts_with_mod.match(glob))
+
+
+def sort_mods(glob):
+ '''Mods are sorted in the keylet.to_string() result so make sure that
+ bind commands also have their mod keys sorted.'''
+
+ mods = []
+ while True:
+ match = starts_with_mod.match(glob)
+ if not match:
+ break
+
+ end = match.span()[1]
+ mods.append(glob[:end])
+ glob = glob[end:]
+
+ return '%s%s' % (''.join(sorted(mods)), glob)
+
+
+def add_instance(uzbl, *args):
+ UZBLS[uzbl] = {'binds': [], 'depth': 0, 'filter': [],
+ 'args': [], 'last_mode': ''}
+
+
+def del_instance(uzbl, *args):
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
+
+
+def get_bind_dict(uzbl):
+ '''Return the bind dict for the uzbl instance.'''
+
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+def get_binds(uzbl):
+ '''Return the bind list for the uzbl instance.'''
+
+ 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):
+ '''Return the bind list for the uzbl instance or return the filtered
+ bind list thats on the current stack.'''
+
+ bind_dict = get_bind_dict(uzbl)
+ if bind_dict['depth']:
+ return list(bind_dict['filter'])
+
+ return list(bind_dict['binds'])
+
+
+def del_bind(uzbl, bind):
+ '''Delete bind object if bind in the uzbl binds.'''
+
+ binds = get_binds(uzbl)
+ if bind in binds:
+ binds.remove(bind)
+ uzbl.event('DELETED_BIND', bind)
+ return True
+
+ return False
+
+
+def del_bind_by_glob(uzbl, glob):
+ '''Delete bind by glob if bind in the uzbl binds.'''
+
+ binds = get_binds(uzbl)
+ for bind in list(binds):
+ if bind.glob == glob:
+ binds.remove(bind)
+ uzbl.event('DELETED_BIND', bind)
+ return True
+
+ return False
+
+
+class Bind(object):
+
+ nextbid = counter().next
+
+ def __init__(self, glob, handler, *args, **kargs):
+ self.callable = iscallable(handler)
+
+ if not glob:
+ raise ArgumentError('glob cannot be blank')
+
+ if self.callable:
+ self.function = handler
+ self.args = args
+ self.kargs = kargs
+
+ elif kargs:
+ raise ArgumentError('cannot supply kargs for uzbl commands')
+
+ elif isiterable(handler):
+ self.commands = handler
+
+ else:
+ self.commands = [handler,] + list(args)
+
+ self.glob = glob
+ self.bid = self.nextbid()
+
+ self.split = split = find_prompts(glob)
+ self.prompts = split[1::2]
+
+ # Check that there is nothing like: fl*<int:>*
+ for glob in split[:-1:2]:
+ if glob.endswith('*'):
+ msg = "token '*' not at the end of a prompt bind: %r" % split
+ raise BindParseError(msg)
+
+ # Check that there is nothing like: fl<prompt1:><prompt2:>_
+ for glob in split[2::2]:
+ if not glob:
+ msg = 'found null segment after first prompt: %r' % split
+ raise BindParseError(msg)
+
+ self.stack = []
+
+ for glob in split[::2]:
+ # Is the binding a MODCMD or KEYCMD:
+ mod_cmd = ismodbind(glob)
+
+ # Execute the command on UPDATES or EXEC's:
+ on_exec = True if glob.endswith('_') else False
+
+ # Does the command store arguments:
+ has_args = True if glob[-1] in ['*', '_'] else False
+ glob = glob[:-1] if has_args else glob
+
+ self.stack.append((mod_cmd, on_exec, has_args, glob))
+
+
+ def __repr__(self):
+ args = ['glob=%r' % self.glob, 'bid=%d' % self.bid]
+
+ if self.callable:
+ args.append('function=%r' % self.function)
+ if self.args:
+ args.append('args=%r' % self.args)
+
+ if self.kargs:
+ args.append('kargs=%r' % self.kargs)
+
+ else:
+ cmdlen = len(self.commands)
+ cmds = self.commands[0] if cmdlen == 1 else self.commands
+ args.append('command%s=%r' % ('s' if cmdlen-1 else '', cmds))
+
+ return '<Bind(%s)>' % ', '.join(args)
+
+
+def bind(uzbl, glob, handler, *args, **kargs):
+ '''Add a bind handler object.'''
+
+ # Mods come from the keycmd sorted so make sure the modkeys in the bind
+ # command are sorted too.
+ glob = sort_mods(glob)
+
+ del_bind_by_glob(uzbl, glob)
+ binds = get_binds(uzbl)
+
+ bind = Bind(glob, handler, *args, **kargs)
+ binds.append(bind)
+
+ print bind
+ uzbl.event('ADDED_BIND', bind)
+
+
+def parse_bind_event(uzbl, args):
+ '''Break "event BIND fl* = js follownums.js" into (glob, command).'''
+
+ if not args:
+ return error('missing bind arguments')
+
+ split = map(unicode.strip, args.split('=', 1))
+ if len(split) != 2:
+ return error('missing "=" in bind definition: %r' % args)
+
+ glob, command = split
+ bind(uzbl, glob, command)
+
+
+def set_stack_mode(uzbl, prompt):
+ if uzbl.get_mode() != 'stack':
+ uzbl.set_mode('stack')
+
+ if prompt:
+ prompt = "%s: " % prompt
+
+ uzbl.set('keycmd_prompt', prompt)
+
+
+def clear_stack(uzbl, mode):
+ bind_dict = get_bind_dict(uzbl)
+ if mode != "stack" and bind_dict['last_mode'] == "stack":
+ uzbl.set('keycmd_prompt', '')
+
+ if mode != "stack":
+ bind_dict = get_bind_dict(uzbl)
+ bind_dict['filter'] = []
+ bind_dict['depth'] = 0
+ bind_dict['args'] = []
+
+ bind_dict['last_mode'] = mode
+
+
+def filter_bind(uzbl, bind_dict, bind):
+ '''Remove a bind from the stack filter list.'''
+
+ if bind in bind_dict['filter']:
+ bind_dict['filter'].remove(bind)
+
+ if not bind_dict['filter']:
+ uzbl.set_mode()
+
+
+def match_and_exec(uzbl, bind, depth, keycmd):
+ bind_dict = get_bind_dict(uzbl)
+ mode_cmd, on_exec, has_args, glob = bind.stack[depth]
+
+ if has_args:
+ if not keycmd.startswith(glob):
+ filter_bind(uzbl, bind_dict, bind)
+ return False
+
+ args = [keycmd[len(glob):],]
+
+ elif keycmd != glob:
+ filter_bind(uzbl, bind_dict, bind)
+ return False
+
+ else:
+ args = []
+
+ execindex = len(bind.stack)-1
+ if execindex == depth == 0:
+ uzbl.exec_handler(bind, *args)
+ if not has_args:
+ uzbl.clear_keycmd()
+
+ return True
+
+ elif depth != execindex:
+ if bind_dict['depth'] == depth:
+ bind_dict['filter'] = [bind,]
+ bind_dict['args'] += args
+ bind_dict['depth'] = depth + 1
+
+ else:
+ if bind not in bind_dict['filter']:
+ bind_dict['filter'].append(bind)
+
+ set_stack_mode(uzbl, bind.prompts[depth])
+ return False
+
+ args = bind_dict['args'] + args
+ uzbl.exec_handler(bind, *args)
+ if on_exec:
+ uzbl.set_mode()
+
+ return True
+
+
+def keycmd_update(uzbl, keylet):
+ depth = get_stack_depth(uzbl)
+ keycmd = keylet.to_string()
+ for bind in get_filtered_binds(uzbl):
+ t = bind.stack[depth]
+ if t[MOD_CMD] or t[ON_EXEC]:
+ continue
+
+ match_and_exec(uzbl, bind, depth, keycmd)
+
+
+def keycmd_exec(uzbl, keylet):
+ depth = get_stack_depth(uzbl)
+ keycmd = keylet.to_string()
+ for bind in get_filtered_binds(uzbl):
+ t = bind.stack[depth]
+ if t[MOD_CMD] or not t[ON_EXEC]:
+ continue
+
+ match_and_exec(uzbl, bind, depth, keycmd)
+
+
+def modcmd_update(uzbl, keylet):
+ depth = get_stack_depth(uzbl)
+ keycmd = keylet.to_string()
+ for bind in get_filtered_binds(uzbl):
+ t = bind.stack[depth]
+ if not t[MOD_CMD] or t[ON_EXEC]:
+ continue
+
+ match_and_exec(uzbl, bind, depth, keycmd)
+
+
+def modcmd_exec(uzbl, keylet):
+ depth = get_stack_depth(uzbl)
+ keycmd = keylet.to_string()
+ for bind in get_filtered_binds(uzbl):
+ t = bind.stack[depth]
+ if not t[MOD_CMD] or not t[ON_EXEC]:
+ continue
+
+ match_and_exec(uzbl, bind, depth, keycmd)
+
+
+def init(uzbl):
+ connects = {'BIND': parse_bind_event,
+ 'KEYCMD_UPDATE': keycmd_update,
+ 'MODCMD_UPDATE': modcmd_update,
+ 'KEYCMD_EXEC': keycmd_exec,
+ 'MODCMD_EXEC': modcmd_exec,
+ 'MODE_CHANGED': clear_stack}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/config.py b/examples/data/uzbl/scripts/plugins/config.py
new file mode 100644
index 0000000..22803b4
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/config.py
@@ -0,0 +1,87 @@
+import re
+import types
+
+__export__ = ['set', 'get_config']
+
+_VALIDSETKEY = re.compile("^[a-zA-Z][a-zA-Z0-9_]*$").match
+_TYPECONVERT = {'int': int, 'float': float, 'str': unicode}
+
+UZBLS = {}
+
+
+def escape(value):
+ '''A real escaping function may be required.'''
+
+ return unicode(value)
+
+
+def get_config(uzbl):
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+def set(uzbl, key, value):
+ '''Sends a: "set key = value" command to the uzbl instance.'''
+
+ if type(value) == types.BooleanType:
+ value = int(value)
+
+ if not _VALIDSETKEY(key):
+ raise KeyError("%r" % key)
+
+ value = escape(value)
+ if '\n' in value:
+ value = value.replace("\n", "\\n")
+
+ uzbl.send('set %s = %s' % (key, value))
+
+
+def add_instance(uzbl, *args):
+ UZBLS[uzbl] = ConfigDict(uzbl)
+
+
+def del_instance(uzbl, *args):
+ if uzbl in UZBLS:
+ del uzbl
+
+
+def get_config(uzbl):
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+class ConfigDict(dict):
+ def __init__(self, uzbl):
+ self._uzbl = uzbl
+
+ def __setitem__(self, key, value):
+ '''Makes "config[key] = value" a wrapper for the set function.'''
+
+ if key not in self or unicode(self[key]) != unicode(value):
+ set(self._uzbl, key, value)
+
+
+def variable_set(uzbl, args):
+ config = get_config(uzbl)
+
+ key, type, value = list(args.split(' ', 2) + ['',])[:3]
+ old = config[key] if key in config else None
+ value = _TYPECONVERT[type](value)
+
+ dict.__setitem__(config, key, value)
+
+ if old != value:
+ uzbl.event("CONFIG_CHANGED", key, value)
+
+
+def init(uzbl):
+
+ connects = {'VARIABLE_SET': variable_set,
+ 'INSTANCE_START': add_instance,
+ 'INSTANCE_EXIT': del_instance}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/keycmd.py b/examples/data/uzbl/scripts/plugins/keycmd.py
new file mode 100644
index 0000000..3dd6f37
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/keycmd.py
@@ -0,0 +1,382 @@
+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 = {}
+
+# Hold the keylets.
+UZBLS = {}
+
+# Simple key names map.
+_SIMPLEKEYS = {
+ 'Control': 'Ctrl',
+ 'ISO_Left_Tab': 'Shift-Tab',
+ 'space':'Space',
+}
+
+# Keycmd format which includes the markup for the cursor.
+KEYCMD_FORMAT = "%s<span @cursor_style>%s</span>%s"
+
+
+def escape(str):
+ '''Prevent outgoing keycmd values from expanding inside the
+ status_format.'''
+
+ if not str:
+ return ''
+
+ for char in ['\\', '@']:
+ if char in str:
+ str = str.replace(char, '\\'+char)
+
+ 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
+ self.held = []
+
+ # to_string() string building cache.
+ self._to_string = None
+
+ self.modcmd = False
+ self.wasmod = False
+
+ def __repr__(self):
+ return '<Keycmd(%r)>' % self.to_string()
+
+
+ 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
+
+ if not self.held:
+ self._to_string = self.cmd
+
+ else:
+ self._to_string = ''.join(['<%s>' % key for key in self.held])
+ if self.cmd:
+ self._to_string += self.cmd
+
+ return self._to_string
+
+
+ def match(self, regex):
+ '''See if the keycmd string matches the given regex.'''
+
+ return bool(get_regex(regex)(self.to_string()))
+
+
+def make_simple(key):
+ '''Make some obscure names for some keys friendlier.'''
+
+ # Remove left-right discrimination.
+ if key.endswith('_L') or key.endswith('_R'):
+ key = key[:-2]
+
+ if key in _SIMPLEKEYS:
+ key = _SIMPLEKEYS[key]
+
+ return key
+
+
+def add_instance(uzbl, *args):
+ '''Create the Keylet object for this uzbl instance.'''
+
+ UZBLS[uzbl] = Keylet()
+
+
+def del_instance(uzbl, *args):
+ '''Delete the Keylet object for this uzbl instance.'''
+
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
+
+
+def get_keylet(uzbl):
+ '''Return the corresponding keylet for this uzbl instance.'''
+
+ # Startup events are not correctly captured and sent over the uzbl socket
+ # yet so this line is needed because the INSTANCE_START event is lost.
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ keylet = UZBLS[uzbl]
+ keylet._to_string = None
+ return keylet
+
+
+def clear_keycmd(uzbl):
+ '''Clear the keycmd for this uzbl instance.'''
+
+ k = get_keylet(uzbl)
+ k.cmd = ''
+ k.cursor = 0
+ k._to_string = None
+
+ if k.modcmd:
+ k.wasmod = True
+
+ k.modcmd = False
+ config = uzbl.get_config()
+ if 'keycmd' not in config or config['keycmd'] != '':
+ config['keycmd'] = ''
+
+ uzbl.event('KEYCMD_CLEAR')
+
+
+def update_event(uzbl, k):
+ '''Raise keycmd & modcmd update events.'''
+
+ config = uzbl.get_config()
+ if k.modcmd:
+ keycmd = k.to_string()
+ uzbl.event('MODCMD_UPDATE', k)
+ if keycmd != k.to_string():
+ return
+
+ if 'modcmd_updates' in config and config['modcmd_updates'] != '1':
+ return
+
+ return uzbl.set('keycmd', escape(keycmd))
+
+ if 'keycmd_events' in config and config['keycmd_events'] != '1':
+ return
+
+ keycmd = k.cmd
+ uzbl.event('KEYCMD_UPDATE', k)
+ if keycmd != k.cmd:
+ return
+
+ 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]
+
+ else:
+ cursor = ' '
+
+ chunks = map(escape, [k.cmd[:k.cursor], cursor, k.cmd[k.cursor+1:]])
+ uzbl.set('keycmd', KEYCMD_FORMAT % tuple(chunks))
+
+
+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.
+ 6. Keycmd is updated and events raised if anything is changed.'''
+
+ if key.startswith('Shift_'):
+ return
+
+ if len(key) > 1:
+ 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:])
+ 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):
+ 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:
+ 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 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
+
+ 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
+
+ if cmdmod:
+ 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.
+ 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'
+
+ elif key in ['Shift', 'Alt'] and 'Meta' in k.held:
+ key = 'Meta'
+
+ if key in k.held:
+ if k.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)
+
+
+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.cursor = len(keycmd)
+ update_event(uzbl, k)
+
+
+def set_cursor_pos(uzbl, index):
+ '''Allow setting of the cursor position externally. Supports negative
+ indexing.'''
+
+ cursor = int(index.strip())
+ k = get_keylet(uzbl)
+
+ if cursor < 0:
+ cursor = len(k.cmd) + cursor
+
+ if cursor < 0:
+ cursor = 0
+
+ if cursor > len(k.cmd):
+ cursor = len(k.cmd)
+
+ k.cursor = cursor
+ update_event(uzbl, k)
+
+
+def init(uzbl):
+ '''Connect handlers to uzbl events.'''
+
+ connects = {'INSTANCE_START': add_instance,
+ 'INSTANCE_EXIT': del_instance,
+ 'KEY_PRESS': key_press,
+ 'KEY_RELEASE': key_release,
+ 'SET_KEYCMD': set_keycmd,
+ 'SET_CURSOR_POS': set_cursor_pos}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/mode.py b/examples/data/uzbl/scripts/plugins/mode.py
new file mode 100644
index 0000000..ad0d9a8
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/mode.py
@@ -0,0 +1,159 @@
+import sys
+import re
+
+__export__ = ['set_mode', 'get_mode']
+
+UZBLS = {}
+
+DEFAULTS = {
+ 'mode': '',
+ 'default': '',
+ 'modes': {
+ 'insert': {
+ 'forward_keys': True,
+ 'keycmd_events': False,
+ 'modcmd_updates': False,
+ 'indicator': 'I'},
+ 'command': {
+ 'forward_keys': False,
+ 'keycmd_events': True,
+ 'modcmd_updates': True,
+ 'indicator': 'C'}}}
+
+_RE_FINDSPACES = re.compile("\s+")
+
+
+def error(msg):
+ sys.stderr.write("mode plugin: error: %s\n" % msg)
+
+
+def add_instance(uzbl, *args):
+ UZBLS[uzbl] = dict(DEFAULTS)
+
+
+def del_instance(uzbl, *args):
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
+
+
+def get_mode_dict(uzbl):
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+def get_mode_config(uzbl, mode):
+ modes = get_mode_dict(uzbl)['modes']
+ if mode not in modes:
+ modes[mode] = {}
+
+ return modes[mode]
+
+
+def get_mode(uzbl):
+ return get_mode_dict(uzbl)['mode']
+
+
+def key_press(uzbl, key):
+ if key != "Escape":
+ return
+
+ set_mode(uzbl)
+
+
+def set_mode(uzbl, mode=None):
+ mode_dict = get_mode_dict(uzbl)
+ if mode is None:
+ if not mode_dict['default']:
+ return error("no default mode to fallback on")
+
+ mode = mode_dict['default']
+
+ config = uzbl.get_config()
+ if 'mode' not in config or config['mode'] != mode:
+ config['mode'] = mode
+
+ mode_dict['mode'] = mode
+ mode_config = get_mode_config(uzbl, mode)
+
+ for (key, value) in mode_config.items():
+ if key not in config:
+ config[key] = value
+
+ elif config[key] != value:
+ config[key] = value
+
+ if 'mode_indicator' not in mode_config:
+ config['mode_indicator'] = mode
+
+ uzbl.clear_keycmd()
+ uzbl.send('update_gui')
+ uzbl.event("MODE_CHANGED", mode)
+
+
+def config_changed(uzbl, key, value):
+ if key == 'default_mode':
+ mode_dict = get_mode_dict(uzbl)
+ mode_dict['default'] = value
+ if value and not mode_dict['mode']:
+ set_mode(uzbl, value)
+
+ elif key == 'mode':
+ if not value:
+ value = None
+
+ set_mode(uzbl, value)
+
+
+def mode_config(uzbl, args):
+
+ split = map(unicode.strip, _RE_FINDSPACES.split(args.lstrip(), 1))
+ if len(split) != 2:
+ return error("invalid MODE_CONFIG syntax: %r" % args)
+
+ mode, set = split
+ split = map(unicode.strip, set.split('=', 1))
+ if len(split) != 2:
+ return error("invalid MODE_CONFIG set command: %r" % args)
+
+ key, value = split
+ mode_config = get_mode_config(uzbl, mode)
+ mode_config[key] = value
+
+ if get_mode(uzbl) == mode:
+ uzbl.set(key, value)
+
+
+def load_reset(uzbl, *args):
+ config = uzbl.get_config()
+ if 'reset_on_commit' not in config or config['reset_on_commit'] == '1':
+ set_mode(uzbl)
+
+
+def toggle_modes(uzbl, modes):
+
+ modelist = [s.strip() for s in modes.split(' ') if s]
+ if not len(modelist):
+ return error("no modes specified to toggle")
+
+ mode_dict = get_mode_dict(uzbl)
+ oldmode = mode_dict['mode']
+ if oldmode not in modelist:
+ return set_mode(uzbl, modelist[0])
+
+ newmode = modelist[(modelist.index(oldmode)+1) % len(modelist)]
+ set_mode(uzbl, newmode)
+
+
+def init(uzbl):
+
+ connects = {'CONFIG_CHANGED': config_changed,
+ 'INSTANCE_EXIT': del_instance,
+ 'INSTANCE_START': add_instance,
+ 'KEY_PRESS': key_press,
+ 'MODE_CONFIG': mode_config,
+ 'LOAD_START': load_reset,
+ 'TOGGLE_MODES': toggle_modes}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/on_event.py b/examples/data/uzbl/scripts/plugins/on_event.py
new file mode 100644
index 0000000..242f9b0
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/on_event.py
@@ -0,0 +1,117 @@
+'''Plugin provides arbitrary binding of uzbl events to uzbl commands.
+
+Formatting options:
+ %@ = space separated string of the arguments
+ %1 = argument 1
+ %2 = argument 2
+ %n = argument n
+
+Usage:
+ request ON_EVENT LINK_HOVER set selected_uri = $1
+ --> LINK_HOVER http://uzbl.org/
+ <-- set selected_uri = http://uzbl.org/
+
+ request ON_EVENT CONFIG_CHANGED print Config changed: %1 = %2
+ --> CONFIG_CHANGED selected_uri http://uzbl.org/
+ <-- print Config changed: selected_uri = http://uzbl.org/
+'''
+
+import sys
+import re
+from event_manager import config
+
+__export__ = ['get_on_events', 'on_event']
+
+UZBLS = {}
+
+def echo(msg):
+ if config['verbose']:
+ print 'on_event plugin:', msg
+
+
+def error(msg):
+ sys.stderr.write('on_event plugin: error: %s\n' % msg)
+
+
+def add_instance(uzbl, *args):
+ UZBLS[uzbl] = {}
+
+
+def del_instance(uzbl, *args):
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
+
+
+def get_on_events(uzbl):
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+def expand(cmd, args):
+ '''Replaces "%@ %1 %2 %3..." with "<all args> <arg 0> <arg 1>...".'''
+
+ if '%@' in cmd:
+ cmd = cmd.replace('%@', ' '.join(map(unicode, args)))
+
+ for (index, arg) in enumerate(args):
+ index += 1
+ if '%%%d' % index in cmd:
+ cmd = cmd.replace('%%%d' % index, unicode(arg))
+
+ return cmd
+
+
+def event_handler(uzbl, *args, **kargs):
+ '''This function handles all the events being watched by various
+ on_event definitions and responds accordingly.'''
+
+ events = get_on_events(uzbl)
+ event = kargs['on_event']
+ if event not in events:
+ return
+
+ commands = events[event]
+ for cmd in commands:
+ cmd = expand(cmd, args)
+ uzbl.send(cmd)
+
+
+def on_event(uzbl, event, cmd):
+ '''Add a new event to watch and respond to.'''
+
+ event = event.upper()
+ events = get_on_events(uzbl)
+ if event not in events:
+ uzbl.connect(event, event_handler, on_event=event)
+ events[event] = []
+
+ cmds = events[event]
+ if cmd not in cmds:
+ cmds.append(cmd)
+
+
+def parse_on_event(uzbl, args):
+ '''Parse ON_EVENT events and pass them to the on_event function.
+
+ Syntax: "event ON_EVENT <EVENT_NAME> commands".'''
+
+ if not args:
+ return error("missing on_event arguments")
+
+ split = args.split(' ', 1)
+ if len(split) != 2:
+ return error("invalid ON_EVENT syntax: %r" % args)
+
+ event, cmd = split
+ on_event(uzbl, event, cmd)
+
+
+def init(uzbl):
+
+ connects = {'ON_EVENT': parse_on_event,
+ 'INSTANCE_START': add_instance,
+ 'INSTANCE_EXIT': del_instance}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/plugins/plugin_template.py b/examples/data/uzbl/scripts/plugins/plugin_template.py
new file mode 100644
index 0000000..03cb748
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/plugin_template.py
@@ -0,0 +1,75 @@
+'''Plugin template.'''
+
+# A list of functions this plugin exports to be used via uzbl object.
+__export__ = ['myplugin_function',]
+
+# Holds the per-instance data dict.
+UZBLS = {}
+
+# The default instance dict.
+DEFAULTS = {}
+
+
+def add_instance(uzbl, *args):
+ '''Add a new instance with default config options.'''
+
+ UZBLS[uzbl] = dict(DEFAULTS)
+
+
+def del_instance(uzbl, *args):
+ '''Delete data stored for an instance.'''
+
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
+
+
+def get_myplugin_dict(uzbl):
+ '''Get data stored for an instance.'''
+
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+def myplugin_function(uzbl, *args, **kargs):
+ '''Custom plugin function which is exported by the __export__ list at the
+ top of the file for use by other functions/callbacks.'''
+
+ print "My plugin function arguments:", args, kargs
+
+ # Get the per-instance data object.
+ data = get_myplugin_dict(uzbl)
+
+ # Function logic goes here.
+
+
+def myplugin_event_parser(uzbl, args):
+ '''Parses MYPLUGIN_EVENT raised by uzbl or another plugin.'''
+
+ print "Got MYPLUGIN_EVENT with arguments: %r" % args
+
+ # Parsing logic goes here.
+
+
+def init(uzbl):
+ '''The main function of the plugin which is used to attach all the event
+ hooks that are going to be used throughout the plugins life. This function
+ is called each time a UzblInstance() object is created in the event
+ manager.'''
+
+ # Make a dictionary comprising of {"EVENT_NAME": handler, ..} to the event
+ # handler stack:
+ connects = {
+ 'INSTANCE_START': add_instance,
+ 'INSTANCE_EXIT': del_instance,
+ 'MYPLUGIN_EVENT': myplugin_event_parser,
+ }
+
+ # And connect the dicts event handlers to the handler stack.
+ uzbl.connect_dict(connects)
+
+ # Or connect a handler to an event manually and supply additional optional
+ # arguments:
+
+ #uzbl.connect("MYOTHER_EVENT", myother_event_parser, True, limit=20)
diff --git a/examples/data/uzbl/scripts/plugins/progress_bar.py b/examples/data/uzbl/scripts/plugins/progress_bar.py
new file mode 100644
index 0000000..b6fcb1b
--- /dev/null
+++ b/examples/data/uzbl/scripts/plugins/progress_bar.py
@@ -0,0 +1,158 @@
+import sys
+
+UZBLS = {}
+
+DEFAULTS = {'width': 8,
+ 'done': '=',
+ 'pending': '.',
+ 'format': '[%d%a%p]%c',
+ 'spinner': '-\\|/',
+ 'sprites': 'loading',
+ 'updates': 0,
+ 'progress': 100}
+
+
+def error(msg):
+ sys.stderr.write("progress_bar plugin: error: %s\n" % msg)
+
+
+def add_instance(uzbl, *args):
+ UZBLS[uzbl] = dict(DEFAULTS)
+
+
+def del_instance(uzbl, *args):
+ if uzbl in UZBLS:
+ del UZBLS[uzbl]
+
+
+def get_progress_config(uzbl):
+ if uzbl not in UZBLS:
+ add_instance(uzbl)
+
+ return UZBLS[uzbl]
+
+
+def update_progress(uzbl, prog=None):
+ '''Updates the progress_format variable on LOAD_PROGRESS update.
+
+ The current substitution options are:
+ %d = done char * done
+ %p = pending char * remaining
+ %c = percent done
+ %i = int done
+ %s = -\|/ spinner
+ %t = percent pending
+ %o = int pending
+ %r = sprites
+ '''
+
+ prog_config = get_progress_config(uzbl)
+ config = uzbl.get_config()
+
+ if prog is None:
+ prog = prog_config['progress']
+
+ else:
+ prog = int(prog)
+ prog_config['progress'] = prog
+
+ prog_config['updates'] += 1
+ format = prog_config['format']
+ width = prog_config['width']
+
+ # Inflate the done and pending bars to stop the progress bar
+ # jumping around.
+ if '%c' in format or '%i' in format:
+ count = format.count('%c') + format.count('%i')
+ width += (3-len(str(prog))) * count
+
+ if '%t' in format or '%o' in format:
+ count = format.count('%t') + format.count('%o')
+ width += (3-len(str(100-prog))) * count
+
+ done = int(((prog/100.0)*width)+0.5)
+ pending = width - done
+
+ if '%d' in format:
+ format = format.replace('%d', prog_config['done']*done)
+
+ if '%p' in format:
+ format = format.replace('%p', prog_config['pending']*pending)
+
+ if '%c' in format:
+ format = format.replace('%c', '%d%%' % prog)
+
+ if '%i' in format:
+ format = format.replace('%i', '%d' % prog)
+
+ if '%t' in format:
+ format = format.replace('%t', '%d%%' % (100-prog))
+
+ if '%o' in format:
+ format = format.replace('%o', '%d' % (100-prog))
+
+ if '%s' in format:
+ spinner = prog_config['spinner']
+ spin = '-' if not spinner else spinner
+ index = 0 if prog == 100 else prog_config['updates'] % len(spin)
+ char = '\\\\' if spin[index] == '\\' else spin[index]
+ format = format.replace('%s', char)
+
+ if '%r' in format:
+ sprites = prog_config['sprites']
+ sprites = '-' if not sprites else sprites
+ index = int(((prog/100.0)*len(sprites))+0.5)-1
+ sprite = '\\\\' if sprites[index] == '\\' else sprites[index]
+ format = format.replace('%r', sprite)
+
+ if 'progress_format' not in config or config['progress_format'] != format:
+ config['progress_format'] = format
+
+
+def progress_config(uzbl, args):
+ '''Parse PROGRESS_CONFIG events from the uzbl instance.
+
+ Syntax: event PROGRESS_CONFIG <key> = <value>
+ '''
+
+ split = args.split('=', 1)
+ if len(split) != 2:
+ return error("invalid syntax: %r" % args)
+
+ key, value = map(unicode.strip, split)
+ prog_config = get_progress_config(uzbl)
+
+ if key not in prog_config:
+ return error("key error: %r" % args)
+
+ if type(prog_config[key]) == type(1):
+ try:
+ value = int(value)
+
+ except:
+ return error("invalid type: %r" % args)
+
+ elif not value:
+ value = ' '
+
+ prog_config[key] = value
+ update_progress(uzbl)
+
+
+def reset_progress(uzbl, args):
+ '''Reset the spinner counter, reset the progress int and re-draw the
+ progress bar on LOAD_COMMIT.'''
+
+ prog_dict = get_progress_config(uzbl)
+ prog_dict['updates'] = prog_dict['progress'] = 0
+ update_progress(uzbl)
+
+
+def init(uzbl):
+ connects = {'LOAD_PROGRESS': update_progress,
+ 'INSTANCE_START': add_instance,
+ 'INSTANCE_EXIT': del_instance,
+ 'PROGRESS_CONFIG': progress_config,
+ 'LOAD_COMMIT': reset_progress}
+
+ uzbl.connect_dict(connects)
diff --git a/examples/data/uzbl/scripts/session.sh b/examples/data/uzbl/scripts/session.sh
index 22d48a2..1059b5e 100755
--- a/examples/data/uzbl/scripts/session.sh
+++ b/examples/data/uzbl/scripts/session.sh
@@ -1,18 +1,18 @@
#!/bin/sh
-# Very simple session manager for uzbl. When called with "endsession" as the
+# Very simple session manager for uzbl-browser. When called with "endsession" as the
# argument, it'll backup $sessionfile, look for fifos in $fifodir and
# instruct each of them to store their current url in $sessionfile and
# terminate themselves. Run with "launch" as the argument and an instance of
-# uzbl will be launched for each stored url. "endinstance" is used internally
+# uzbl-browser will be launched for each stored url. "endinstance" is used internally
# and doesn't need to be called manually at any point.
# Add a line like 'bind quit = /path/to/session.sh endsession' to your config
[ -d ${XDG_DATA_HOME:-$HOME/.local/share}/uzbl ] || exit 1
scriptfile=$0 # this script
-sessionfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/session # the file in which the "session" (i.e. urls) are stored
+sessionfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/browser-session # the file in which the "session" (i.e. urls) are stored
configfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/config # uzbl configuration file
-UZBL="uzbl -c $configfile" # add custom flags and whatever here.
+UZBL="uzbl-browser -c $configfile" # add custom flags and whatever here.
fifodir=/tmp # remember to change this if you instructed uzbl to put its fifos elsewhere
thisfifo="$4"
diff --git a/examples/data/uzbl/scripts/uzbl_tabbed.py b/examples/data/uzbl/scripts/uzbl_tabbed.py
index cd5ef4f..bb9b9a2 100755
--- a/examples/data/uzbl/scripts/uzbl_tabbed.py
+++ b/examples/data/uzbl/scripts/uzbl_tabbed.py
@@ -983,7 +983,7 @@ class UzblTabbed:
uri = "--uri %r" % uri
self.tabs[tab] = uzbl
- cmd = 'uzbl -s %s -n %s_%0.2d %s &' % (sid, self.wid, pid, uri)
+ cmd = 'uzbl-browser -s %s -n %s_%0.2d %s &' % (sid, self.wid, pid, uri)
subprocess.Popen([cmd], shell=True) # TODO: do i need close_fds=True ?
# Add gobject timer to make sure the config is pushed when fifo socket
diff --git a/inspector.c b/inspector.c
new file mode 100644
index 0000000..de3dbcd
--- /dev/null
+++ b/inspector.c
@@ -0,0 +1,103 @@
+/*
+ ** WebInspector
+ ** (c) 2009 by Robert Manea
+*/
+
+#include "uzbl-core.h"
+#include "events.h"
+#include "callbacks.h"
+
+
+void
+hide_window_cb(GtkWidget *widget, gpointer data) {
+ (void) data;
+
+ gtk_widget_hide(widget);
+}
+
+WebKitWebView*
+create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
+ (void) data;
+ (void) page;
+ (void) web_inspector;
+ GtkWidget* scrolled_window;
+ GtkWidget* new_web_view;
+ GUI *g = &uzbl.gui;
+
+ g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
+ G_CALLBACK(hide_window_cb), NULL);
+
+ gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
+ gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
+ gtk_widget_show(g->inspector_window);
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
+ gtk_widget_show(scrolled_window);
+
+ new_web_view = webkit_web_view_new();
+ gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
+
+ return WEBKIT_WEB_VIEW(new_web_view);
+}
+
+gboolean
+inspector_show_window_cb (WebKitWebInspector* inspector){
+ (void) inspector;
+ gtk_widget_show(uzbl.gui.inspector_window);
+
+ send_event(WEBINSPECTOR, "open", NULL);
+ return TRUE;
+}
+
+/* TODO: Add variables and code to make use of these functions */
+gboolean
+inspector_close_window_cb (WebKitWebInspector* inspector){
+ (void) inspector;
+ send_event(WEBINSPECTOR, "close", NULL);
+ return TRUE;
+}
+
+gboolean
+inspector_attach_window_cb (WebKitWebInspector* inspector){
+ (void) inspector;
+ return FALSE;
+}
+
+gboolean
+inspector_detach_window_cb (WebKitWebInspector* inspector){
+ (void) inspector;
+ return FALSE;
+}
+
+gboolean
+inspector_uri_changed_cb (WebKitWebInspector* inspector){
+ (void) inspector;
+ return FALSE;
+}
+
+gboolean
+inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
+ (void) inspector;
+ return FALSE;
+}
+
+void
+set_up_inspector() {
+ GUI *g = &uzbl.gui;
+ WebKitWebSettings *settings = view_settings();
+ g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
+
+ uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
+ g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
+ g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
+ g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
+ g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
+ g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
+ g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
+
+ g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
+}
diff --git a/inspector.h b/inspector.h
new file mode 100644
index 0000000..57d0ca9
--- /dev/null
+++ b/inspector.h
@@ -0,0 +1,7 @@
+/*
+ ** WebInspector
+ ** (c) 2009 by Robert Manea
+*/
+
+void
+set_up_inspector();
diff --git a/tests/Makefile b/tests/Makefile
index 9db398a..3f63adf 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,4 +1,4 @@
-CFLAGS:=-std=c99 -I$(shell pwd)/../ -L$(shell pwd) -luzbl $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT="\"$(shell git log | head -n1 | sed "s/.* //")\"" $(CPPFLAGS)
+CFLAGS:=-std=c99 -I$(shell pwd)/../ -L$(shell pwd) -luzbl-core $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT="\"$(shell git log | head -n1 | sed "s/.* //")\"" $(CPPFLAGS)
CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT='"\""'`git log | head -n1 | sed "s/.* //"`'"\""' $(CPPFLAGS)
LDFLAGS:=$(shell pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -pthread $(LDFLAGS)
@@ -14,4 +14,4 @@ all: $(TEST_PROGS)
clean:
rm -f $(TEST_PROGS)
- rm -f libuzbl.so
+ rm -f libuzbl-core.so
diff --git a/tests/test-command.c b/tests/test-command.c
index aee4bf2..2a226b2 100644
--- a/tests/test-command.c
+++ b/tests/test-command.c
@@ -22,10 +22,10 @@
#include <fcntl.h>
#include <signal.h>
-#include <uzbl.h>
+#include <uzbl-core.h>
#include <config.h>
-extern Uzbl uzbl;
+extern UzblCore uzbl;
void
test_keycmd (void) {
@@ -35,13 +35,13 @@ test_keycmd (void) {
/* the 'keycmd' command */
parse_command("keycmd", "insert", NULL);
- g_assert_cmpint(1, ==, uzbl.behave.insert_mode);
+ g_assert_cmpint(1, ==, uzbl.behave.forward_keys);
g_assert_cmpstr("", ==, uzbl.state.keycmd);
/* setting the keycmd variable directly, equivalent to the 'keycmd' comand */
set_var_value("keycmd", "command");
- g_assert_cmpint(0, ==, uzbl.behave.insert_mode);
+ g_assert_cmpint(0, ==, uzbl.behave.forward_keys);
g_assert_cmpstr("", ==, uzbl.state.keycmd);
}
diff --git a/tests/test-expand.c b/tests/test-expand.c
index 2299227..f01e5c7 100644
--- a/tests/test-expand.c
+++ b/tests/test-expand.c
@@ -22,10 +22,10 @@
#include <fcntl.h>
#include <signal.h>
-#include <uzbl.h>
+#include <uzbl-core.h>
#include <config.h>
-extern Uzbl uzbl;
+extern UzblCore uzbl;
extern gchar* expand(char*, guint);
extern void make_var_to_name_hash(void);
@@ -79,10 +79,10 @@ test_NAME (void) {
void
test_MODE (void) {
- set_var_value("insert_mode", "0");
+ set_var_value("forward_keys", "0");
g_assert_cmpstr(expand("@MODE", 0), ==, "C");
- set_var_value("insert_mode", "1");
+ set_var_value("forward_keys", "1");
g_assert_cmpstr(expand("@MODE", 0), ==, "I");
}
diff --git a/uzbl-browser b/uzbl-browser
new file mode 100755
index 0000000..202db11
--- /dev/null
+++ b/uzbl-browser
@@ -0,0 +1,38 @@
+#!/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
+
+# 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..
+
+
+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
+
+if [ ! -S $XDG_CACHE_HOME/uzbl/cookie_daemon_socket ]
+then
+ $XDG_DATA_HOME/uzbl/scripts/cookie_daemon.py
+fi
+
+
+SOCKET_ID="$RANDOM$RANDOM"
+SOCKET_DIR="/tmp"
+SOCKET_PATH="$SOCKET_DIR/uzbl_socket_$SOCKET_ID"
+
+uzbl-core "$@" -n $SOCKET_ID &
+$XDG_DATA_HOME/uzbl/scripts/event_manager.py -vs $SOCKET_PATH
+
+# TODO: make posix sh compliant. [ -S ] is said to not work. what about test -S ?
+if [[ -S $SOCKETPATH ]]
+then
+ rm $SOCKET_PATH
+fi
diff --git a/uzbl.c b/uzbl-core.c
index b7cc165..a4f096b 100644
--- a/uzbl.c
+++ b/uzbl-core.c
@@ -29,39 +29,13 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
-#define LENGTH(x) (sizeof x / sizeof x[0])
-#define _POSIX_SOURCE
-
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-#include <gdk/gdkkeysyms.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <sys/utsname.h>
-#include <sys/time.h>
-#include <webkit/webkit.h>
-#include <libsoup/soup.h>
-#include <JavaScriptCore/JavaScript.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <assert.h>
-#include <poll.h>
-#include <sys/uio.h>
-#include <sys/ioctl.h>
-#include <assert.h>
-#include "uzbl.h"
+#include "uzbl-core.h"
+#include "callbacks.h"
+#include "events.h"
+#include "inspector.h"
#include "config.h"
-Uzbl uzbl;
+UzblCore uzbl;
/* commandline arguments (set initial values for the state variables) */
const
@@ -84,9 +58,17 @@ GOptionEntry entries[] =
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
-enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
+XDG_Var XDG[] =
+{
+ { "XDG_CONFIG_HOME", "~/.config" },
+ { "XDG_DATA_HOME", "~/.local/share" },
+ { "XDG_CACHE_HOME", "~/.cache" },
+ { "XDG_CONFIG_DIRS", "/etc/xdg" },
+ { "XDG_DATA_DIRS", "/usr/local/share/:/usr/share/" },
+};
/* associate command names to their properties */
+enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
typedef struct {
enum ptr_type type;
union {
@@ -117,24 +99,16 @@ const struct var_name_to_ptr_t {
{ "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)},
{ "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)},
{ "geometry", PTR_V_STR(uzbl.gui.geometry, 1, cmd_set_geometry)},
- { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
+ { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, update_title)},
{ "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
{ "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
{ "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
{ "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
- { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
- { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
- { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
{ "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
- { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
- { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
{ "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
{ "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
{ "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
- { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
- { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
- { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
- { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
+ { "forward_keys", PTR_V_INT(uzbl.behave.forward_keys, 1, NULL)},
{ "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
{ "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
{ "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
@@ -150,6 +124,8 @@ const struct var_name_to_ptr_t {
{ "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
{ "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
{ "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
+ /* requires webkit >=1.1.14 */
+ //{ "view_source", PTR_V_INT(uzbl.behave.view_source, 0, cmd_view_source)},
/* exported WebKitWebSettings properties */
{ "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
@@ -182,43 +158,16 @@ const struct var_name_to_ptr_t {
{ "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
{ "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
{ "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
- { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
{ "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
{ "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
- { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
{ "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
+ { "PID", PTR_C_STR(uzbl.info.pid_str, NULL)},
{ NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
};
-
-
-const struct {
- /*@null@*/ char *key;
- guint mask;
-} modkeys[] = {
- { "SHIFT", GDK_SHIFT_MASK }, // shift
- { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
- { "CONTROL", GDK_CONTROL_MASK }, // control
- { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
- { "MOD2", GDK_MOD2_MASK }, // 5th mod
- { "MOD3", GDK_MOD3_MASK }, // 6th mod
- { "MOD4", GDK_MOD4_MASK }, // 7th mod
- { "MOD5", GDK_MOD5_MASK }, // 8th mod
- { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
- { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
- { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
- { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
- { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
- { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
- { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
- { "META", GDK_META_MASK }, // meta (since 2.10)
- { NULL, 0 }
-};
-
-
/* construct a hash from the var_name_to_ptr array for quick access */
void
-make_var_to_name_hash() {
+create_var_to_name_hash() {
const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
while(n2v_p->name) {
@@ -229,6 +178,7 @@ make_var_to_name_hash() {
}
}
+
/* --- UTILITY FUNCTIONS --- */
enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
enum exp_type
@@ -252,7 +202,7 @@ return EXP_ERR;
/*
* recurse == 1: don't expand '@(command)@'
* recurse == 2: don't expand '@<java script>@'
- */
+*/
gchar *
expand(const char *s, guint recurse) {
uzbl_cmdprop *c;
@@ -397,7 +347,10 @@ itos(int val) {
}
gchar*
-strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
+strfree(gchar *str) {
+ g_free(str);
+ return NULL;
+}
gchar*
argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
@@ -409,7 +362,7 @@ str_replace (const char* search, const char* replace, const char* string) {
buf = g_strsplit (string, search, -1);
ret = g_strjoinv (replace, buf);
- g_strfreev(buf); // somebody said this segfaults
+ g_strfreev(buf);
return ret;
}
@@ -423,7 +376,6 @@ read_file_by_line (const gchar *path) {
int i = 0;
chan = g_io_channel_new_file(path, "r", NULL);
-
if (chan) {
while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
const gchar* val = g_strdup (readbuf);
@@ -440,6 +392,56 @@ read_file_by_line (const gchar *path) {
return lines;
}
+/* search a PATH style string for an existing file+path combination */
+gchar*
+find_existing_file(gchar* path_list) {
+ int i=0;
+ int cnt;
+ gchar **split;
+ gchar *tmp = NULL;
+ gchar *executable;
+
+ if(!path_list)
+ return NULL;
+
+ split = g_strsplit(path_list, ":", 0);
+ while(split[i])
+ i++;
+
+ if(i<=1) {
+ tmp = g_strdup(split[0]);
+ g_strfreev(split);
+ return tmp;
+ }
+ else
+ cnt = i-1;
+
+ i=0;
+ tmp = g_strdup(split[cnt]);
+ g_strstrip(tmp);
+ if(tmp[0] == '/')
+ executable = g_strdup_printf("%s", tmp+1);
+ else
+ executable = g_strdup(tmp);
+ g_free(tmp);
+
+ while(i<cnt) {
+ tmp = g_strconcat(g_strstrip(split[i]), "/", executable, NULL);
+ if(g_file_test(tmp, G_FILE_TEST_EXISTS)) {
+ g_strfreev(split);
+ return tmp;
+ }
+ else
+ g_free(tmp);
+ i++;
+ }
+
+ g_free(executable);
+ g_strfreev(split);
+ return NULL;
+}
+
+
gchar*
parseenv (char* string) {
extern char** environ;
@@ -482,19 +484,22 @@ setup_signal(int signr, sigfunc *shandler) {
void
clean_up(void) {
+ send_event(INSTANCE_EXIT, uzbl.info.pid_str, NULL);
+ g_free(uzbl.info.pid_str);
+
+ g_free(uzbl.state.executable_path);
+ g_hash_table_destroy(uzbl.behave.commands);
+
+ if(uzbl.state.event_buffer)
+ g_ptr_array_free(uzbl.state.event_buffer, TRUE);
+
if (uzbl.behave.fifo_dir)
unlink (uzbl.comm.fifo_path);
if (uzbl.behave.socket_dir)
unlink (uzbl.comm.socket_path);
-
- g_free(uzbl.state.executable_path);
- g_free(uzbl.state.keycmd);
- g_hash_table_destroy(uzbl.bindings);
- g_hash_table_destroy(uzbl.behave.commands);
}
/* --- SIGNAL HANDLER --- */
-
void
catch_sigterm(int s) {
(void) s;
@@ -508,115 +513,11 @@ catch_sigint(int s) {
exit(EXIT_SUCCESS);
}
-/* --- CALLBACKS --- */
-
-gboolean
-navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
- (void) web_view;
- (void) frame;
- (void) navigation_action;
- (void) user_data;
-
- const gchar* uri = webkit_network_request_get_uri (request);
- gboolean decision_made = FALSE;
-
- if (uzbl.state.verbose)
- printf("Navigation requested -> %s\n", uri);
-
- if (uzbl.behave.scheme_handler) {
- GString *s = g_string_new ("");
- g_string_printf(s, "'%s'", uri);
-
- run_handler(uzbl.behave.scheme_handler, s->str);
-
- if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
- char *p = strchr(uzbl.comm.sync_stdout, '\n' );
- if ( p != NULL ) *p = '\0';
- if (!strcmp(uzbl.comm.sync_stdout, "USED")) {
- webkit_web_policy_decision_ignore(policy_decision);
- decision_made = TRUE;
- }
- }
- if (uzbl.comm.sync_stdout)
- uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
-
- g_string_free(s, TRUE);
- }
- if (!decision_made)
- webkit_web_policy_decision_use(policy_decision);
-
- return TRUE;
-}
-
-gboolean
-new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
- (void) web_view;
- (void) frame;
- (void) navigation_action;
- (void) policy_decision;
- (void) user_data;
- const gchar* uri = webkit_network_request_get_uri (request);
- if (uzbl.state.verbose)
- printf("New window requested -> %s \n", uri);
- webkit_web_policy_decision_use(policy_decision);
- return TRUE;
-}
-
-gboolean
-mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
- (void) frame;
- (void) request;
- (void) user_data;
-
- /* If we can display it, let's display it... */
- if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
- webkit_web_policy_decision_use (policy_decision);
- return TRUE;
- }
-
- /* ...everything we can't displayed is downloaded */
- webkit_web_policy_decision_download (policy_decision);
- return TRUE;
-}
-
-/*@null@*/ WebKitWebView*
-create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
- (void) web_view;
- (void) frame;
- (void) user_data;
- if (uzbl.state.selected_url != NULL) {
- if (uzbl.state.verbose)
- printf("\nNew web view -> %s\n",uzbl.state.selected_url);
- new_window_load_uri(uzbl.state.selected_url);
- } else {
- if (uzbl.state.verbose)
- printf("New web view -> %s\n","Nothing to open, exiting");
- }
- return (NULL);
-}
-
-gboolean
-download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
- (void) web_view;
- (void) user_data;
- if (uzbl.behave.download_handler) {
- const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
- if (uzbl.state.verbose)
- printf("Download -> %s\n",uri);
- /* if urls not escaped, we may have to escape and quote uri before this call */
-
- GString *args = g_string_new(uri);
-
- if (uzbl.net.proxy_url) {
- g_string_append_c(args, ' ');
- g_string_append(args, uzbl.net.proxy_url);
- }
-
- run_handler(uzbl.behave.download_handler, args->str);
-
- g_string_free(args, TRUE);
- }
- return (FALSE);
+void
+catch_sigalrm(int s) {
+ (void) s;
+ g_ptr_array_free(uzbl.state.event_buffer, TRUE);
+ uzbl.state.event_buffer = NULL;
}
/* scroll a bar in a given direction */
@@ -667,136 +568,6 @@ scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
scroll(uzbl.gui.bar_h, argv);
}
-void
-cmd_set_geometry() {
- if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
- if(uzbl.state.verbose)
- printf("Error in geometry string: %s\n", uzbl.gui.geometry);
- }
- /* update geometry var with the actual geometry
- this is necessary as some WMs don't seem to honour
- the above setting and we don't want to end up with
- wrong geometry information
- */
- retrieve_geometry();
-}
-
-void
-cmd_set_status() {
- if (!uzbl.behave.show_status) {
- gtk_widget_hide(uzbl.gui.mainbar);
- } else {
- gtk_widget_show(uzbl.gui.mainbar);
- }
- update_title();
-}
-
-void
-toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
- (void)page;
- (void)argv;
- (void)result;
-
- webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
-}
-
-void
-toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
- (void)page;
- (void)argv;
- (void)result;
-
- if (uzbl.behave.show_status) {
- gtk_widget_hide(uzbl.gui.mainbar);
- } else {
- gtk_widget_show(uzbl.gui.mainbar);
- }
- uzbl.behave.show_status = !uzbl.behave.show_status;
- update_title();
-}
-
-void
-link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
- (void) page;
- (void) title;
- (void) data;
- //Set selected_url state variable
- g_free(uzbl.state.selected_url);
- uzbl.state.selected_url = NULL;
- if (link) {
- uzbl.state.selected_url = g_strdup(link);
- }
- update_title();
-}
-
-void
-title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
- (void) web_view;
- (void) param_spec;
- const gchar *title = webkit_web_view_get_title(web_view);
- if (uzbl.gui.main_title)
- g_free (uzbl.gui.main_title);
- uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
- update_title();
-}
-
-void
-progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
- (void) page;
- (void) data;
- uzbl.gui.sbar.load_progress = progress;
-
- g_free(uzbl.gui.sbar.progress_bar);
- uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
-
- update_title();
-}
-
-void
-load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
- (void) page;
- (void) frame;
- (void) data;
- if (uzbl.behave.load_finish_handler)
- run_handler(uzbl.behave.load_finish_handler, "");
-}
-
-void clear_keycmd() {
- g_free(uzbl.state.keycmd);
- uzbl.state.keycmd = g_strdup("");
-}
-
-void
-load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
- (void) page;
- (void) frame;
- (void) data;
- uzbl.gui.sbar.load_progress = 0;
- if (uzbl.behave.load_start_handler)
- run_handler(uzbl.behave.load_start_handler, "");
-}
-
-void
-load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
- (void) page;
- (void) data;
- g_free (uzbl.state.uri);
- GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
- uzbl.state.uri = g_string_free (newuri, FALSE);
- if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
- set_insert_mode(uzbl.behave.always_insert_mode);
- update_title();
- }
- if (uzbl.behave.load_commit_handler)
- run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
-}
-
-void
-destroy_cb (GtkWidget* widget, gpointer data) {
- (void) widget;
- (void) data;
- gtk_main_quit ();
-}
/* VIEW funcs (little webkit wrappers) */
@@ -812,43 +583,43 @@ 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} },
+ { "set", {set_var, TRUE} },
+ { "dump_config", {act_dump_config, 0} },
+ { "dump_config_as_events", {act_dump_config_as_events, 0} },
+ { "chain", {chain, 0} },
+ { "print", {print, TRUE} },
+ { "event", {event, TRUE} },
+ /* a request is just semantic sugar to make things more obvious for
+ * the user, technically events and requests are the very same thing
+ */
+ { "request", {event, TRUE} },
+ { "update_gui", {update_gui, TRUE} }
};
void
@@ -910,6 +681,23 @@ update_gui(WebKitWebView *page, GArray *argv, GString *result) {
}
void
+event(WebKitWebView *page, GArray *argv, GString *result) {
+ (void) page; (void) result;
+ GString *event_name;
+ gchar **split = g_strsplit(argv_idx(argv, 0), " ", 2);
+
+ if(split[0])
+ event_name = g_string_ascii_up(g_string_new(split[0]));
+ else
+ return;
+
+ send_event(0, split[1]?split[1]:"", event_name->str);
+
+ g_string_free(event_name, TRUE);
+ g_strfreev(split);
+}
+
+void
print(WebKitWebView *page, GArray *argv, GString *result) {
(void) page; (void) result;
gchar* buf;
@@ -920,60 +708,13 @@ print(WebKitWebView *page, GArray *argv, GString *result) {
}
void
-act_bind(WebKitWebView *page, GArray *argv, GString *result) {
- (void) page; (void) result;
- gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
- gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
- add_binding(g_strstrip(split[0]), value);
- g_free(value);
- g_strfreev(split);
-}
-
-
-void
act_dump_config() {
dump_config();
}
void
-set_keycmd() {
- run_keycmd(FALSE);
- update_title();
-}
-
-void
-set_mode_indicator() {
- uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
- uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
-}
-
-void
-update_indicator() {
- set_mode_indicator();
- update_title();
-}
-
-void
-set_insert_mode(gboolean mode) {
- uzbl.behave.insert_mode = mode;
- set_mode_indicator();
-}
-
-void
-toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
- (void) page; (void) result;
-
- if (argv_idx(argv, 0)) {
- if (strcmp (argv_idx(argv, 0), "0") == 0) {
- set_insert_mode(FALSE);
- } else {
- set_insert_mode(TRUE);
- }
- } else {
- set_insert_mode( !uzbl.behave.insert_mode );
- }
-
- update_title();
+act_dump_config_as_events() {
+ dump_config_as_events();
}
void
@@ -986,8 +727,25 @@ load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
run_js(web_view, argv, NULL);
return;
}
- if (!soup_uri_new(argv_idx(argv, 0)))
- g_string_prepend (newuri, "http://");
+ if (!soup_uri_new(argv_idx(argv, 0))) {
+ GString* fullpath = g_string_new ("");
+ if (g_path_is_absolute (newuri->str))
+ g_string_assign (fullpath, newuri->str);
+ else {
+ gchar* wd;
+ wd = g_get_current_dir ();
+ g_string_assign (fullpath, g_build_filename (wd, newuri->str, NULL));
+ free(wd);
+ }
+ struct stat stat_result;
+ if (! g_stat(fullpath->str, &stat_result)) {
+ g_string_prepend (fullpath, "file://");
+ g_string_assign (newuri, fullpath->str);
+ }
+ else
+ g_string_prepend (newuri, "http://");
+ g_string_free (fullpath, TRUE);
+ }
/* if we do handle cookies, ask our handler for them */
webkit_web_view_load_uri (web_view, newuri->str);
g_string_free (newuri, TRUE);
@@ -1097,8 +855,11 @@ run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
void
run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
(void) result;
- if (argv_idx(argv, 0)) {
- GArray* lines = read_file_by_line (argv_idx (argv, 0));
+ gchar *path = NULL;
+
+ if (argv_idx(argv, 0) &&
+ ((path = find_existing_file(argv_idx(argv, 0)))) ) {
+ GArray* lines = read_file_by_line (path);
gchar* js = NULL;
int i = 0;
gchar* line;
@@ -1125,6 +886,7 @@ run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
eval_js (web_view, js, result);
g_free (js);
g_array_free (lines, TRUE);
+ g_free(path);
}
}
@@ -1171,6 +933,7 @@ new_window_load_uri (const gchar * uri) {
GString *s = g_string_new ("");
g_string_printf(s, "'%s'", uri);
run_handler(uzbl.behave.new_window, s->str);
+ send_event(NEW_WINDOW, s->str, NULL);
return;
}
GString* to_execute = g_string_new ("");
@@ -1187,6 +950,8 @@ new_window_load_uri (const gchar * uri) {
if (uzbl.state.verbose)
printf("\n%s\n", to_execute->str);
g_spawn_command_line_async (to_execute->str, NULL);
+ /* TODO: should we just report the uri as event detail? */
+ send_event(NEW_WINDOW, to_execute->str, NULL);
g_string_free (to_execute, TRUE);
}
@@ -1205,39 +970,6 @@ chain (WebKitWebView *page, GArray *argv, GString *result) {
}
void
-keycmd (WebKitWebView *page, GArray *argv, GString *result) {
- (void)page;
- (void)argv;
- (void)result;
- uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
- run_keycmd(FALSE);
- update_title();
-}
-
-void
-keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
- (void)page;
- (void)argv;
- (void)result;
- uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
- run_keycmd(TRUE);
- update_title();
-}
-
-void
-keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
- gchar *prev;
- (void)page;
- (void)argv;
- (void)result;
- int len = strlen(uzbl.state.keycmd);
- prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
- if (prev)
- uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
- update_title();
-}
-
-void
close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
(void)page;
(void)argv;
@@ -1245,27 +977,6 @@ close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
gtk_main_quit ();
}
-/* --Statusbar functions-- */
-char*
-build_progressbar_ascii(int percent) {
- int width=uzbl.gui.sbar.progress_w;
- int i;
- double l;
- GString *bar = g_string_new("");
-
- l = (double)percent*((double)width/100.);
- l = (int)(l+.5)>=(int)l ? l+.5 : l;
-
- for(i=0; i<(int)l; i++)
- g_string_append(bar, uzbl.gui.sbar.progress_s);
-
- for(; i<width; i++)
- g_string_append(bar, uzbl.gui.sbar.progress_u);
-
- return g_string_free(bar, FALSE);
-}
-/* --End Statusbar functions-- */
-
void
sharg_append(GArray *a, const gchar *str) {
const gchar *s = (str ? str : "");
@@ -1370,9 +1081,15 @@ split_quoted(const gchar* src, const gboolean unquote) {
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))
- run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
+ if ( argv_idx(argv, 0) &&
+ ((path = find_existing_file(argv_idx(argv, 0)))) ) {
+ run_command(path, 0,
+ ((const gchar **) (argv->data + sizeof(gchar*))),
+ FALSE, NULL);
+ g_free(path);
+ }
}
void
@@ -1573,285 +1290,6 @@ parse_command(const char *cmd, const char *param, GString *result) {
g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
}
-void
-set_proxy_url() {
- SoupURI *suri;
-
- if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
- soup_session_remove_feature_by_type(uzbl.net.soup_session,
- (GType) SOUP_SESSION_PROXY_URI);
- }
- else {
- suri = soup_uri_new(uzbl.net.proxy_url);
- g_object_set(G_OBJECT(uzbl.net.soup_session),
- SOUP_SESSION_PROXY_URI,
- suri, NULL);
- soup_uri_free(suri);
- }
- return;
-}
-
-void
-set_icon() {
- if(file_exists(uzbl.gui.icon)) {
- if (uzbl.gui.main_window)
- gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
- } else {
- g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
- }
-}
-
-void
-cmd_load_uri() {
- GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
- g_array_append_val (a, uzbl.state.uri);
- load_uri(uzbl.gui.web_view, a, NULL);
- g_array_free (a, TRUE);
-}
-
-void
-cmd_always_insert_mode() {
- set_insert_mode(uzbl.behave.always_insert_mode);
- update_title();
-}
-
-void
-cmd_max_conns() {
- g_object_set(G_OBJECT(uzbl.net.soup_session),
- SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
-}
-
-void
-cmd_max_conns_host() {
- g_object_set(G_OBJECT(uzbl.net.soup_session),
- SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
-}
-
-void
-cmd_http_debug() {
- soup_session_remove_feature
- (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
- /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
- /*g_free(uzbl.net.soup_logger);*/
-
- uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
- soup_session_add_feature(uzbl.net.soup_session,
- SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
-}
-
-WebKitWebSettings*
-view_settings() {
- return webkit_web_view_get_settings(uzbl.gui.web_view);
-}
-
-void
-cmd_font_size() {
- WebKitWebSettings *ws = view_settings();
- if (uzbl.behave.font_size > 0) {
- g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
- }
-
- if (uzbl.behave.monospace_size > 0) {
- g_object_set (G_OBJECT(ws), "default-monospace-font-size",
- uzbl.behave.monospace_size, NULL);
- } else {
- g_object_set (G_OBJECT(ws), "default-monospace-font-size",
- uzbl.behave.font_size, NULL);
- }
-}
-
-void
-cmd_default_font_family() {
- g_object_set (G_OBJECT(view_settings()), "default-font-family",
- uzbl.behave.default_font_family, NULL);
-}
-
-void
-cmd_monospace_font_family() {
- g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
- uzbl.behave.monospace_font_family, NULL);
-}
-
-void
-cmd_sans_serif_font_family() {
- g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
- uzbl.behave.sans_serif_font_family, NULL);
-}
-
-void
-cmd_serif_font_family() {
- g_object_set (G_OBJECT(view_settings()), "serif-font-family",
- uzbl.behave.serif_font_family, NULL);
-}
-
-void
-cmd_cursive_font_family() {
- g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
- uzbl.behave.cursive_font_family, NULL);
-}
-
-void
-cmd_fantasy_font_family() {
- g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
- uzbl.behave.fantasy_font_family, NULL);
-}
-
-void
-cmd_zoom_level() {
- webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
-}
-
-void
-cmd_disable_plugins() {
- g_object_set (G_OBJECT(view_settings()), "enable-plugins",
- !uzbl.behave.disable_plugins, NULL);
-}
-
-void
-cmd_disable_scripts() {
- g_object_set (G_OBJECT(view_settings()), "enable-scripts",
- !uzbl.behave.disable_scripts, NULL);
-}
-
-void
-cmd_minimum_font_size() {
- g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
- uzbl.behave.minimum_font_size, NULL);
-}
-void
-cmd_autoload_img() {
- g_object_set (G_OBJECT(view_settings()), "auto-load-images",
- uzbl.behave.autoload_img, NULL);
-}
-
-
-void
-cmd_autoshrink_img() {
- g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
- uzbl.behave.autoshrink_img, NULL);
-}
-
-
-void
-cmd_enable_spellcheck() {
- g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
- uzbl.behave.enable_spellcheck, NULL);
-}
-
-void
-cmd_enable_private() {
- g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
- uzbl.behave.enable_private, NULL);
-}
-
-void
-cmd_print_bg() {
- g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
- uzbl.behave.print_bg, NULL);
-}
-
-void
-cmd_style_uri() {
- g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
- uzbl.behave.style_uri, NULL);
-}
-
-void
-cmd_resizable_txt() {
- g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
- uzbl.behave.resizable_txt, NULL);
-}
-
-void
-cmd_default_encoding() {
- g_object_set (G_OBJECT(view_settings()), "default-encoding",
- uzbl.behave.default_encoding, NULL);
-}
-
-void
-cmd_enforce_96dpi() {
- g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
- uzbl.behave.enforce_96dpi, NULL);
-}
-
-void
-cmd_caret_browsing() {
- g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
- uzbl.behave.caret_browsing, NULL);
-}
-
-void
-cmd_cookie_handler() {
- gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
- /* pitfall: doesn't handle chain actions; must the sync_ action manually */
- if ((g_strcmp0(split[0], "sh") == 0) ||
- (g_strcmp0(split[0], "spawn") == 0)) {
- g_free (uzbl.behave.cookie_handler);
- uzbl.behave.cookie_handler =
- g_strdup_printf("sync_%s %s", split[0], split[1]);
- }
- g_strfreev (split);
-}
-
-void
-cmd_scheme_handler() {
- gchar **split = g_strsplit(uzbl.behave.scheme_handler, " ", 2);
- /* pitfall: doesn't handle chain actions; must the sync_ action manually */
- if ((g_strcmp0(split[0], "sh") == 0) ||
- (g_strcmp0(split[0], "spawn") == 0)) {
- g_free (uzbl.behave.scheme_handler);
- uzbl.behave.scheme_handler =
- g_strdup_printf("sync_%s %s", split[0], split[1]);
- }
- g_strfreev (split);
-}
-
-void
-cmd_fifo_dir() {
- uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
-}
-
-void
-cmd_socket_dir() {
- uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
-}
-
-void
-cmd_inject_html() {
- if(uzbl.behave.inject_html) {
- webkit_web_view_load_html_string (uzbl.gui.web_view,
- uzbl.behave.inject_html, NULL);
- }
-}
-
-void
-cmd_modkey() {
- int i;
- char *buf;
-
- buf = g_utf8_strup(uzbl.behave.modkey, -1);
- uzbl.behave.modmask = 0;
-
- if(uzbl.behave.modkey)
- g_free(uzbl.behave.modkey);
- uzbl.behave.modkey = buf;
-
- for (i = 0; modkeys[i].key != NULL; i++) {
- if (g_strrstr(buf, modkeys[i].key))
- uzbl.behave.modmask |= modkeys[i].mask;
- }
-}
-
-void
-cmd_useragent() {
- if (*uzbl.net.useragent == ' ') {
- g_free (uzbl.net.useragent);
- uzbl.net.useragent = NULL;
- } else {
- g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
- uzbl.net.useragent, NULL);
- }
-}
void
move_statusbar() {
@@ -1884,22 +1322,32 @@ set_var_value(const gchar *name, gchar *val) {
char *endp = NULL;
char *buf = NULL;
char *invalid_chars = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
+ GString *msg;
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 = g_strdup(val);
g_free(*c->ptr.s);
*c->ptr.s = buf;
+ g_string_append_printf(msg, " str %s", buf);
+
} else if(c->type == TYPE_INT) {
*c->ptr.i = (int)strtoul(val, &endp, 10);
+ g_string_append_printf(msg, " int %d", *c->ptr.i);
} else if (c->type == TYPE_FLOAT) {
*c->ptr.f = strtod(val, &endp);
+ g_string_append_printf(msg, " float %f", *c->ptr.f);
}
+ send_event(VARIABLE_SET, msg->str, NULL);
+ g_string_free(msg,TRUE);
+
/* invoke a command specific function */
if(c->func) c->func();
} else {
@@ -1921,6 +1369,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, NULL);
+ g_string_free(msg,TRUE);
}
return TRUE;
}
@@ -1943,7 +1396,7 @@ parse_cmd_line(const char *ctl_line, GString *result) {
g_free(work_string);
if( strcmp(g_strchug(ctlstrip), "") &&
- strcmp(exp_line = expand(ctlstrip, 0), "")
+ strcmp(exp_line = expand(ctlstrip, 0), "")
) {
/* ignore comments */
if((exp_line[0] == '#'))
@@ -2026,6 +1479,7 @@ init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
if (uzbl.state.verbose)
printf ("init_fifo: created successfully as %s\n", path);
+ send_event(FIFO_SET, path, NULL);
uzbl.comm.fifo_path = path;
return dir;
} else g_warning ("init_fifo: could not add watch on %s\n", path);
@@ -2080,14 +1534,16 @@ control_socket(GIOChannel *chan) {
struct sockaddr_un remote;
unsigned int t = sizeof(remote);
int clientsock;
- GIOChannel *clientchan;
clientsock = accept (g_io_channel_unix_get_fd(chan),
(struct sockaddr *) &remote, &t);
- if ((clientchan = g_io_channel_unix_new(clientsock))) {
- g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
- (GIOFunc) control_client_socket, clientchan);
+ if ((uzbl.comm.clientchan = g_io_channel_unix_new(clientsock))) {
+ g_io_channel_set_encoding(uzbl.comm.clientchan, NULL, NULL);
+ g_io_add_watch(uzbl.comm.clientchan, G_IO_IN|G_IO_HUP,
+ (GIOFunc) control_client_socket, uzbl.comm.clientchan);
+ /* replay buffered events */
+ send_event_socket(NULL);
}
return TRUE;
@@ -2163,6 +1619,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, NULL);
return dir;
}
} else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
@@ -2214,165 +1671,29 @@ update_title (void) {
}
}
-gboolean
-configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
- (void) window;
- (void) event;
-
- retrieve_geometry();
- return FALSE;
-}
-
-gboolean
-key_press_cb (GtkWidget* window, GdkEventKey* event)
-{
- //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
-
- (void) window;
-
- if (event->type != GDK_KEY_PRESS ||
- event->keyval == GDK_Page_Up ||
- event->keyval == GDK_Page_Down ||
- event->keyval == GDK_Home ||
- event->keyval == GDK_End ||
- event->keyval == GDK_Up ||
- event->keyval == GDK_Down ||
- event->keyval == GDK_Left ||
- event->keyval == GDK_Right ||
- event->keyval == GDK_Shift_L ||
- event->keyval == GDK_Shift_R)
- return FALSE;
-
- /* turn off insert mode (if always_insert_mode is not used) */
- if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
- set_insert_mode(uzbl.behave.always_insert_mode);
- update_title();
- return TRUE;
- }
-
- if (uzbl.behave.insert_mode &&
- ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
- (!uzbl.behave.modmask)
- )
- )
- return FALSE;
-
- if (event->keyval == GDK_Escape) {
- clear_keycmd();
- update_title();
- dehilight(uzbl.gui.web_view, NULL, NULL);
- return TRUE;
- }
-
- //Insert without shift - insert from clipboard; Insert with shift - insert from primary
- if (event->keyval == GDK_Insert) {
- gchar * str;
- if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
- str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
- } else {
- str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
- }
- if (str) {
- GString* keycmd = g_string_new(uzbl.state.keycmd);
- g_string_append (keycmd, str);
- uzbl.state.keycmd = g_string_free(keycmd, FALSE);
- update_title ();
- g_free (str);
- }
- return TRUE;
- }
-
- if (event->keyval == GDK_BackSpace)
- keycmd_bs(NULL, NULL, NULL);
-
- gboolean key_ret = FALSE;
- if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
- key_ret = TRUE;
- if (!key_ret) {
- GString* keycmd = g_string_new(uzbl.state.keycmd);
- g_string_append(keycmd, event->string);
- uzbl.state.keycmd = g_string_free(keycmd, FALSE);
- }
-
- run_keycmd(key_ret);
- update_title();
- if (key_ret) return (!uzbl.behave.insert_mode);
- return TRUE;
-}
-
-void
-run_keycmd(const gboolean key_ret) {
- /* run the keycmd immediately if it isn't incremental and doesn't take args */
- Action *act;
- if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
- clear_keycmd();
- parse_command(act->name, act->param, NULL);
- return;
- }
-
- /* try if it's an incremental keycmd or one that takes args, and run it */
- GString* short_keys = g_string_new ("");
- GString* short_keys_inc = g_string_new ("");
- guint i;
- guint len = strlen(uzbl.state.keycmd);
- for (i=0; i<len; i++) {
- g_string_append_c(short_keys, uzbl.state.keycmd[i]);
- g_string_assign(short_keys_inc, short_keys->str);
- g_string_append_c(short_keys, '_');
- g_string_append_c(short_keys_inc, '*');
-
- if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
- /* run normal cmds only if return was pressed */
- exec_paramcmd(act, i);
- clear_keycmd();
- break;
- } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
- if (key_ret) /* just quit the incremental command on return */
- clear_keycmd();
- else exec_paramcmd(act, i); /* otherwise execute the incremental */
- break;
- }
-
- g_string_truncate(short_keys, short_keys->len - 1);
- }
- g_string_free (short_keys, TRUE);
- g_string_free (short_keys_inc, TRUE);
-}
-
-void
-exec_paramcmd(const Action *act, const guint i) {
- GString *parampart = g_string_new (uzbl.state.keycmd);
- GString *actionname = g_string_new ("");
- GString *actionparam = g_string_new ("");
- g_string_erase (parampart, 0, i+1);
- if (act->name)
- g_string_printf (actionname, act->name, parampart->str);
- if (act->param)
- g_string_printf (actionparam, act->param, parampart->str);
- parse_command(actionname->str, actionparam->str, NULL);
- g_string_free(actionname, TRUE);
- g_string_free(actionparam, TRUE);
- g_string_free(parampart, TRUE);
-}
-
-
void
create_browser () {
GUI *g = &uzbl.gui;
g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
- g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
- g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "navigation-policy-decision-requested", G_CALLBACK (navigation_decision_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
- g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
+ g_object_connect((GObject*)g->web_view,
+ "signal::key-press-event", (GCallback)key_press_cb, NULL,
+ "signal::key-release-event", (GCallback)key_release_cb, NULL,
+ "signal::title-changed", (GCallback)title_change_cb, NULL,
+ "signal::selection-changed", (GCallback)selection_changed_cb, NULL,
+ "signal::load-progress-changed", (GCallback)progress_change_cb, NULL,
+ "signal::load-committed", (GCallback)load_commit_cb, NULL,
+ "signal::load-started", (GCallback)load_start_cb, NULL,
+ "signal::load-finished", (GCallback)load_finish_cb, NULL,
+ "signal::load-error", (GCallback)load_error_cb, NULL,
+ "signal::hovering-over-link", (GCallback)link_hover_cb, NULL,
+ "signal::navigation-policy-decision-requested", (GCallback)navigation_decision_cb, NULL,
+ "signal::new-window-policy-decision-requested", (GCallback)new_window_cb, NULL,
+ "signal::download-requested", (GCallback)download_cb, NULL,
+ "signal::create-web-view", (GCallback)create_web_view_cb, NULL,
+ "signal::mime-type-policy-decision-requested", (GCallback)mime_policy_cb, NULL,
+ NULL);
}
GtkWidget*
@@ -2380,17 +1701,18 @@ create_mainbar () {
GUI *g = &uzbl.gui;
g->mainbar = gtk_hbox_new (FALSE, 0);
-
- /* keep a reference to the bar so we can re-pack it at runtime*/
- //sbar_ref = g_object_ref(g->mainbar);
-
g->mainbar_label = gtk_label_new ("");
gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
- g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
+
+ g_object_connect((GObject*)g->mainbar,
+ "signal::key-press-event", (GCallback)key_press_cb, NULL,
+ "signal::key-release-event", (GCallback)key_release_cb, NULL,
+ NULL);
+
return g->mainbar;
}
@@ -2400,8 +1722,6 @@ create_window () {
gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
gtk_widget_set_name (window, "Uzbl browser");
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
- g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
- g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
return window;
}
@@ -2411,6 +1731,7 @@ create_plug () {
GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
+ g_signal_connect (G_OBJECT (plug), "key-release-event", G_CALLBACK (key_release_cb), NULL);
return plug;
}
@@ -2519,25 +1840,6 @@ run_handler (const gchar *act, const gchar *args) {
g_strfreev(parts);
}
-void
-add_binding (const gchar *key, const gchar *act) {
- char **parts = g_strsplit(act, " ", 2);
- Action *action;
-
- if (!parts)
- return;
-
- //Debug:
- if (uzbl.state.verbose)
- printf ("Binding %-10s : %s\n", key, act);
- action = new_action(parts[0], parts[1]);
-
- if (g_hash_table_remove (uzbl.bindings, key))
- g_warning ("Overwriting existing binding for \"%s\"", key);
- g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
- g_strfreev(parts);
-}
-
/*@null@*/ gchar*
get_xdg_var (XDG_Var xdg) {
const gchar* actual_value = getenv (xdg.environmental);
@@ -2595,6 +1897,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);
@@ -2631,19 +1934,22 @@ settings_init () {
void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
(void) session;
(void) user_data;
- if (!uzbl.behave.cookie_handler)
- return;
+ //if (!uzbl.behave.cookie_handler)
+ // return;
soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
GString *s = g_string_new ("");
SoupURI * soup_uri = soup_message_get_uri(msg);
g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
- run_handler(uzbl.behave.cookie_handler, s->str);
+ if(uzbl.behave.cookie_handler)
+ run_handler(uzbl.behave.cookie_handler, s->str);
- if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
+ if(uzbl.behave.cookie_handler &&
+ uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
char *p = strchr(uzbl.comm.sync_stdout, '\n' );
if ( p != NULL ) *p = '\0';
soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
+
}
if (uzbl.comm.sync_stdout)
uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
@@ -2668,98 +1974,6 @@ save_cookies (SoupMessage *msg, gpointer user_data){
g_slist_free(ck);
}
-/* --- WEBINSPECTOR --- */
-void
-hide_window_cb(GtkWidget *widget, gpointer data) {
- (void) data;
-
- gtk_widget_hide(widget);
-}
-
-WebKitWebView*
-create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
- (void) data;
- (void) page;
- (void) web_inspector;
- GtkWidget* scrolled_window;
- GtkWidget* new_web_view;
- GUI *g = &uzbl.gui;
-
- g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
- G_CALLBACK(hide_window_cb), NULL);
-
- gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
- gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
- gtk_widget_show(g->inspector_window);
-
- scrolled_window = gtk_scrolled_window_new(NULL, NULL);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
- gtk_widget_show(scrolled_window);
-
- new_web_view = webkit_web_view_new();
- gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
-
- return WEBKIT_WEB_VIEW(new_web_view);
-}
-
-gboolean
-inspector_show_window_cb (WebKitWebInspector* inspector){
- (void) inspector;
- gtk_widget_show(uzbl.gui.inspector_window);
- return TRUE;
-}
-
-/* TODO: Add variables and code to make use of these functions */
-gboolean
-inspector_close_window_cb (WebKitWebInspector* inspector){
- (void) inspector;
- return TRUE;
-}
-
-gboolean
-inspector_attach_window_cb (WebKitWebInspector* inspector){
- (void) inspector;
- return FALSE;
-}
-
-gboolean
-inspector_detach_window_cb (WebKitWebInspector* inspector){
- (void) inspector;
- return FALSE;
-}
-
-gboolean
-inspector_uri_changed_cb (WebKitWebInspector* inspector){
- (void) inspector;
- return FALSE;
-}
-
-gboolean
-inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
- (void) inspector;
- return FALSE;
-}
-
-void
-set_up_inspector() {
- GUI *g = &uzbl.gui;
- WebKitWebSettings *settings = view_settings();
- g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
-
- uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
- g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
- g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
- g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
- g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
- g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
- g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
-
- g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
-}
-
void
dump_var_hash(gpointer k, gpointer v, gpointer ud) {
(void) ud;
@@ -2777,18 +1991,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, NULL);
+ 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
@@ -2827,9 +2059,6 @@ initialize(int argc, char *argv[]) {
exit(EXIT_SUCCESS);
}
- /* initialize hash table */
- uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
-
uzbl.net.soup_session = webkit_get_default_session();
uzbl.state.keycmd = g_strdup("");
@@ -2838,13 +2067,12 @@ initialize(int argc, char *argv[]) {
if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
fprintf(stderr, "uzbl: error hooking SIGINT\n");
- uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
- uzbl.gui.sbar.progress_u = g_strdup("·");
- uzbl.gui.sbar.progress_w = 10;
-
- /* default mode indicators */
- uzbl.behave.insert_indicator = g_strdup("I");
- uzbl.behave.cmd_indicator = g_strdup("C");
+ /* Set up the timer for the event buffer */
+ if(setup_signal(SIGALRM, catch_sigalrm) == SIG_ERR) {
+ fprintf(stderr, "uzbl: error hooking SIGALRM\n");
+ exit(EXIT_FAILURE);
+ }
+ event_buffer_timeout(10);
uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
@@ -2853,7 +2081,7 @@ initialize(int argc, char *argv[]) {
uzbl.info.commit = COMMIT;
commands_hash ();
- make_var_to_name_hash();
+ create_var_to_name_hash();
create_browser();
}
@@ -2896,6 +2124,11 @@ main (int argc, char* argv[]) {
if(!uzbl.state.instance_name)
uzbl.state.instance_name = itos((int)uzbl.xwin);
+ GString *tmp = g_string_new("");
+ g_string_printf(tmp, "%d", getpid());
+ uzbl.info.pid_str = g_string_free(tmp, FALSE);
+ send_event(INSTANCE_START, uzbl.info.pid_str, NULL);
+
gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
if (uzbl.state.verbose) {
@@ -2930,9 +2163,6 @@ main (int argc, char* argv[]) {
settings_init ();
- if (!uzbl.behave.always_insert_mode)
- set_insert_mode(FALSE);
-
if (!uzbl.behave.show_status)
gtk_widget_hide(uzbl.gui.mainbar);
else
diff --git a/uzbl.h b/uzbl-core.h
index 810076d..a7a8126 100644
--- a/uzbl.h
+++ b/uzbl-core.h
@@ -7,18 +7,44 @@
*
* (c) 2009 by Robert Manea
* - introduced struct concept
- * - statusbar template
*
*/
+#define _POSIX_SOURCE
+
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+#include <webkit/webkit.h>
+#include <libsoup/soup.h>
+#include <JavaScriptCore/JavaScript.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <assert.h>
+#include <poll.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+
+#define LENGTH(x) (sizeof x / sizeof x[0])
+
/* status bar elements */
typedef struct {
gint load_progress;
gchar *msg;
- gchar *progress_s, *progress_u;
- int progress_w;
- gchar *progress_bar;
- gchar *mode_indicator;
} StatusBar;
@@ -56,6 +82,7 @@ typedef struct {
GHashTable *proto_var;
gchar *sync_stdout;
+ GIOChannel *clientchan;
} Communication;
@@ -66,10 +93,12 @@ typedef struct {
int socket_id;
char *instance_name;
gchar *selected_url;
+ gchar *last_selected_url;
gchar *executable_path;
gchar* keycmd;
gchar* searchtx;
gboolean verbose;
+ GPtrArray *event_buffer;
} State;
@@ -105,15 +134,13 @@ typedef struct {
gchar* fantasy_font_family;
gchar* cursive_font_family;
gchar* scheme_handler;
- gboolean always_insert_mode;
gboolean show_status;
- gboolean insert_mode;
+ gboolean forward_keys;
gboolean status_top;
- gboolean reset_command_mode;
- gchar* modkey;
guint modmask;
guint http_debug;
gchar* shell_cmd;
+ guint view_source;
/* WebKitWebSettings exports */
guint font_size;
guint monospace_size;
@@ -134,12 +161,13 @@ typedef struct {
guint caret_browsing;
guint mode;
gchar* base_url;
- gchar* insert_indicator;
- gchar* cmd_indicator;
gboolean print_version;
- /* command list: name -> Command */
+ /* command list: (key)name -> (value)Command */
+ /* command list: (key)name -> (value)Command */
GHashTable* commands;
+ /* event lookup: (key)event_id -> (value)event_name */
+ GHashTable *event_lookup;
} Behaviour;
/* javascript */
@@ -156,6 +184,7 @@ typedef struct {
int webkit_micro;
gchar *arch;
gchar *commit;
+ gchar *pid_str;
} Info;
/* main uzbl data structure */
@@ -169,34 +198,24 @@ typedef struct {
Info info;
Window xwin;
+} UzblCore;
- /* group bindings: key -> action */
- GHashTable* bindings;
-} Uzbl;
+/* Main Uzbl object */
+extern UzblCore uzbl;
+typedef void sigfunc(int);
typedef struct {
char* name;
char* param;
} Action;
-typedef void sigfunc(int);
-
/* XDG Stuff */
-
typedef struct {
gchar* environmental;
gchar* default_value;
} XDG_Var;
-XDG_Var XDG[] =
-{
- { "XDG_CONFIG_HOME", "~/.config" },
- { "XDG_DATA_HOME", "~/.local/share" },
- { "XDG_CACHE_HOME", "~/.cache" },
- { "XDG_CONFIG_DIRS", "/etc/xdg" },
- { "XDG_DATA_DIRS", "/usr/local/share/:/usr/share/" },
-};
/* Functions */
char *
@@ -205,6 +224,9 @@ itos(int val);
char *
str_replace (const char* search, const char* replace, const char* string);
+gchar*
+strfree(gchar *str);
+
GArray*
read_file_by_line (const gchar *path);
@@ -220,54 +242,15 @@ catch_sigterm(int s);
sigfunc *
setup_signal(int signe, sigfunc *shandler);
+gchar*
+parseenv (char* string);
+
gboolean
set_var_value(const gchar *name, gchar *val);
void
print(WebKitWebView *page, GArray *argv, GString *result);
-gboolean
-navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data);
-
-gboolean
-new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data);
-
-gboolean
-mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data);
-
-/*@null@*/ WebKitWebView*
-create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data);
-
-gboolean
-download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data);
-
-void
-toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result);
-
-void
-toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result);
-
-void
-link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data);
-
-void
-title_change_cb (WebKitWebView* web_view, GParamSpec param_spec);
-
-void
-progress_change_cb (WebKitWebView* page, gint progress, gpointer data);
-
-void
-load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data);
-
-void
-load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data);
-
-void
-load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data);
-
-void
-destroy_cb (GtkWidget* widget, gpointer data);
-
void
commands_hash(void);
@@ -284,18 +267,6 @@ void
set_keycmd();
void
-set_mode_indicator();
-
-void
-update_indicator();
-
-void
-set_insert_mode(gboolean mode);
-
-void
-toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result);
-
-void
load_uri (WebKitWebView * web_view, GArray *argv, GString *result);
void
@@ -305,24 +276,12 @@ void
chain (WebKitWebView *page, GArray *argv, GString *result);
void
-keycmd (WebKitWebView *page, GArray *argv, GString *result);
-
-void
-keycmd_nl (WebKitWebView *page, GArray *argv, GString *result);
-
-void
-keycmd_bs (WebKitWebView *page, GArray *argv, GString *result);
-
-void
close_uzbl (WebKitWebView *page, GArray *argv, GString *result);
gboolean
run_command(const gchar *command, const guint npre,
const gchar **args, const gboolean sync, char **output_stdout);
-char*
-build_progressbar_ascii(int percent);
-
void
talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result);
@@ -374,11 +333,11 @@ update_title (void);
gboolean
key_press_cb (GtkWidget* window, GdkEventKey* event);
-void
-run_keycmd(const gboolean key_ret);
+gboolean
+key_release_cb (GtkWidget* window, GdkEventKey* event);
void
-exec_paramcmd(const Action* act, const guint i);
+run_keycmd(const gboolean key_ret);
void
initialize (int argc, char *argv[]);
@@ -398,9 +357,6 @@ create_plug ();
void
run_handler (const gchar *act, const gchar *args);
-void
-add_binding (const gchar *key, const gchar *act);
-
/*@null@*/ gchar*
get_xdg_var (XDG_Var xdg);
@@ -435,17 +391,16 @@ void handle_cookies (SoupSession *session,
SoupMessage *msg,
gpointer user_data);
void
-save_cookies (SoupMessage *msg,
- gpointer user_data);
+save_cookies (SoupMessage *msg, gpointer user_data);
void
set_var(WebKitWebView *page, GArray *argv, GString *result);
void
-act_bind(WebKitWebView *page, GArray *argv, GString *result);
+act_dump_config();
void
-act_dump_config();
+act_dump_config_as_events();
void
dump_var_hash(gpointer k, gpointer v, gpointer ud);
@@ -457,13 +412,16 @@ void
dump_config();
void
+dump_config_as_events();
+
+void
retrieve_geometry();
void
update_gui(WebKitWebView *page, GArray *argv, GString *result);
-gboolean
-configure_event_cb(GtkWidget* window, GdkEventConfigure* event);
+void
+event(WebKitWebView *page, GArray *argv, GString *result);
typedef void (*Command)(WebKitWebView*, GArray *argv, GString *result);
typedef struct {
@@ -471,121 +429,4 @@ typedef struct {
gboolean no_split;
} CommandInfo;
-/* Command callbacks */
-void
-cmd_load_uri();
-
-void
-cmd_set_status();
-
-void
-set_proxy_url();
-
-void
-set_icon();
-
-void
-cmd_cookie_handler();
-
-void
-cmd_scheme_handler();
-
-void
-move_statusbar();
-
-void
-cmd_always_insert_mode();
-
-void
-cmd_http_debug();
-
-void
-cmd_max_conns();
-
-void
-cmd_max_conns_host();
-
-/* exported WebKitWebSettings properties */
-
-void
-cmd_font_size();
-
-void
-cmd_default_font_family();
-
-void
-cmd_monospace_font_family();
-
-void
-cmd_sans_serif_font_family();
-
-void
-cmd_serif_font_family();
-
-void
-cmd_cursive_font_family();
-
-void
-cmd_fantasy_font_family();
-
-void
-cmd_zoom_level();
-
-void
-cmd_disable_plugins();
-
-void
-cmd_disable_scripts();
-
-void
-cmd_minimum_font_size();
-
-void
-cmd_fifo_dir();
-
-void
-cmd_socket_dir();
-
-void
-cmd_modkey();
-
-void
-cmd_useragent() ;
-
-void
-cmd_autoload_img();
-
-void
-cmd_autoshrink_img();
-
-void
-cmd_enable_spellcheck();
-
-void
-cmd_enable_private();
-
-void
-cmd_print_bg();
-
-void
-cmd_style_uri();
-
-void
-cmd_resizable_txt();
-
-void
-cmd_default_encoding();
-
-void
-cmd_enforce_96dpi();
-
-void
-cmd_inject_html();
-
-void
-cmd_caret_browsing();
-
-void
-cmd_set_geometry();
-
/* vi: set et ts=4: */