From 42dfc6726218dcfd761bc02e7a90d2e49ed0cdef Mon Sep 17 00:00:00 2001 From: Todd Harbour Date: Sat, 11 Jul 2009 08:34:10 +1000 Subject: Mouse support (buttons and wheels) (override) Added support for mouse buttons 1 - 8 and wheel up, down, left and right. --- uzbl.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ uzbl.h | 14 +++++++- 2 files changed, 117 insertions(+), 11 deletions(-) diff --git a/uzbl.c b/uzbl.c index ed9c4be..c3d0cfc 100644 --- a/uzbl.c +++ b/uzbl.c @@ -498,6 +498,9 @@ mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequ (void) request; (void) user_data; + if (uzbl.state.verbose) + printf("MIME Type: %s \n", mime_type); + /* 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); @@ -1083,7 +1086,7 @@ keycmd (WebKitWebView *page, GArray *argv, GString *result) { (void)argv; (void)result; g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0)); - run_keycmd(FALSE); + run_keycmd(FALSE, uzbl.state.keycmd); update_title(); } @@ -1093,7 +1096,7 @@ keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) { (void)argv; (void)result; g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0)); - run_keycmd(TRUE); + run_keycmd(TRUE, uzbl.state.keycmd); update_title(); } @@ -2188,18 +2191,101 @@ key_press_cb (GtkWidget* window, GdkEventKey* event) key_ret = TRUE; if (!key_ret) g_string_append(uzbl.state.keycmd, event->string); - run_keycmd(key_ret); + run_keycmd(key_ret, uzbl.state.keycmd); update_title(); if (key_ret) return (!uzbl.behave.insert_mode); return TRUE; } +static gboolean +mouse_button_cb (GtkWidget* window, GdkEventButton* event) +{ + GString* mousecmd = g_string_new(""); + + (void) window; + + if (event->type != GDK_BUTTON_PRESS || event->button >= 10) + return FALSE; + + g_string_assign(mousecmd, "button + '0'); + g_string_append_c(mousecmd, '>'); + + if (uzbl.state.verbose) + printf("Mouse Button: %s\n", mousecmd->str); + + if (g_hash_table_lookup(uzbl.bindings, mousecmd->str)) { + run_keycmd(FALSE, mousecmd); + g_string_free(mousecmd, TRUE); + return TRUE; + } + + return FALSE; +} + +static gboolean +wk_mouse_button_cb (WebKitWebView *web_view, GdkEventButton* event) +{ + (void) web_view; + + return mouse_button_cb(NULL, event); +} + +static gboolean +mouse_scroll_cb (GtkWidget* window, GdkEventScroll* event) +{ + GString* mousecmd = g_string_new(""); + g_string_assign(mousecmd, "direction) { + case GDK_SCROLL_UP: + g_string_append(mousecmd, "up>"); + break; + case GDK_SCROLL_DOWN: + g_string_append(mousecmd, "down>"); + break; + + case GDK_SCROLL_LEFT: + g_string_append(mousecmd, "left>"); + break; + + case GDK_SCROLL_RIGHT: + g_string_append(mousecmd, "right>"); + break; + + default: + g_string_free(mousecmd, TRUE); + return FALSE; + } + + if (uzbl.state.verbose) + printf("Mouse Scroll: %s\n", mousecmd->str); + + if (g_hash_table_lookup(uzbl.bindings, mousecmd->str)) { + run_keycmd(FALSE, mousecmd); + g_string_free(mousecmd, TRUE); + return TRUE; + } + + return FALSE; +} + +static gboolean +wk_mouse_scroll_cb (WebKitWebView *web_view, GdkEventScroll* event) +{ + (void) web_view; + + return mouse_scroll_cb(NULL, event); +} + static void -run_keycmd(const gboolean key_ret) { +run_keycmd(const gboolean key_ret, GString* keycmd) { /* 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->str))) { - g_string_truncate(uzbl.state.keycmd, 0); + if ((act = g_hash_table_lookup(uzbl.bindings, keycmd->str))) { + g_string_truncate(keycmd, 0); parse_command(act->name, act->param, NULL); return; } @@ -2208,8 +2294,8 @@ run_keycmd(const gboolean key_ret) { GString* short_keys = g_string_new (""); GString* short_keys_inc = g_string_new (""); guint i; - for (i=0; i<(uzbl.state.keycmd->len); i++) { - g_string_append_c(short_keys, uzbl.state.keycmd->str[i]); + for (i=0; i<(keycmd->len); i++) { + g_string_append_c(short_keys, keycmd->str[i]); g_string_assign(short_keys_inc, short_keys->str); g_string_append_c(short_keys, '_'); g_string_append_c(short_keys_inc, '*'); @@ -2217,11 +2303,11 @@ run_keycmd(const gboolean key_ret) { 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); - g_string_truncate(uzbl.state.keycmd, 0); + g_string_truncate(keycmd, 0); break; } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) { if (key_ret) /* just quit the incremental command on return */ - g_string_truncate(uzbl.state.keycmd, 0); + g_string_truncate(keycmd, 0); else exec_paramcmd(act, i); /* otherwise execute the incremental */ break; } @@ -2271,6 +2357,8 @@ create_browser () { 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_signal_connect (G_OBJECT (g->web_view), "button-press-event", G_CALLBACK (wk_mouse_button_cb), g->web_view); + g_signal_connect (G_OBJECT (g->web_view), "scroll-event", G_CALLBACK (wk_mouse_scroll_cb), g->web_view); return scrolled_window; } @@ -2291,6 +2379,8 @@ create_mainbar () { 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_signal_connect (G_OBJECT (g->mainbar), "button-press-event", G_CALLBACK (mouse_button_cb), NULL); + g_signal_connect (G_OBJECT (g->mainbar), "scroll-event", G_CALLBACK (mouse_scroll_cb), NULL); return g->mainbar; } @@ -2301,6 +2391,8 @@ GtkWidget* create_window () { 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), "button-press-event", G_CALLBACK (mouse_button_cb), NULL); + g_signal_connect (G_OBJECT (window), "scroll-event", G_CALLBACK (mouse_scroll_cb), NULL); return window; } @@ -2310,6 +2402,8 @@ GtkPlug* 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), "button-press-event", G_CALLBACK (mouse_button_cb), NULL); + g_signal_connect (G_OBJECT (plug), "scroll-event", G_CALLBACK (mouse_scroll_cb), NULL); return plug; } diff --git a/uzbl.h b/uzbl.h index d19ceb3..b690343 100644 --- a/uzbl.h +++ b/uzbl.h @@ -384,8 +384,20 @@ update_title (void); static gboolean key_press_cb (GtkWidget* window, GdkEventKey* event); +static gboolean +mouse_button_cb (GtkWidget* window, GdkEventButton* event); + +static gboolean +wk_mouse_button_cb (WebKitWebView *web_view, GdkEventButton* event); + +static gboolean +mouse_scroll_cb (GtkWidget* window, GdkEventScroll* event); + +static gboolean +wk_mouse_scroll_cb (WebKitWebView *web_view, GdkEventScroll* event); + static void -run_keycmd(const gboolean key_ret); +run_keycmd(const gboolean key_ret, GString* keycmd); static void exec_paramcmd(const Action* act, const guint i); -- cgit v1.2.3 From 34583bee859bcc7316c3a448765c16f3484eb532 Mon Sep 17 00:00:00 2001 From: Todd Harbour Date: Sun, 19 Jul 2009 09:53:17 +1000 Subject: Greatly improved mouse support, added sample config Some bug fixing and improvements and a sample config. Available binds are: , _, , , _, , , --- examples/config/uzbl/config | 15 +++ uzbl.c | 230 ++++++++++++++++++++++++++++++++++++++------ uzbl.h | 2 +- 3 files changed, 215 insertions(+), 32 deletions(-) diff --git a/examples/config/uzbl/config b/examples/config/uzbl/config index ab2cf7f..6cd257c 100644 --- a/examples/config/uzbl/config +++ b/examples/config/uzbl/config @@ -33,6 +33,21 @@ set modkey = Mod1 # like this you can enter any command at runtime, interactively. prefixed by ':' bind :_ = chain '%s' +# Open links in new window when middle-clicked +bind _ = spawn uzbl -u %s +# Open link in clipboard (current window), when middle-clicking any part +# of the page (that isn't a link) +bind = sh 'echo "uri `xclip -selection clipboard -o`" > $4' +# Back and Forward buttons +bind = back +bind = forward +# Increase the amount of page scroll +bind = scroll_vert -100 +bind = scroll_vert 100 +# wheel-up/down on status bar moves to top and bottom of page +bind = scroll_begin +bind = scroll_end + bind j = scroll_vert 20 bind k = scroll_vert -20 bind h = scroll_horz -20 diff --git a/uzbl.c b/uzbl.c index d932013..763df6e 100644 --- a/uzbl.c +++ b/uzbl.c @@ -539,9 +539,6 @@ mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequ (void) request; (void) user_data; - if (uzbl.state.verbose) - printf("MIME Type: %s \n", mime_type); - /* 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); @@ -725,11 +722,9 @@ load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) { run_handler(uzbl.behave.load_finish_handler, ""); } -void clear_keycmd(gchar** keycmd) { - if (keycmd && *keycmd) { - g_free(*keycmd); - *keycmd = g_strdup(""); - } +void clear_keycmd() { + g_free(uzbl.state.keycmd); + uzbl.state.keycmd = g_strdup(""); } void @@ -738,7 +733,7 @@ load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) { (void) frame; (void) data; uzbl.gui.sbar.load_progress = 0; - clear_keycmd(&uzbl.state.keycmd); // don't need old commands to remain on new page? + clear_keycmd(); // don't need old commands to remain on new page? if (uzbl.behave.load_start_handler) run_handler(uzbl.behave.load_start_handler, ""); } @@ -908,7 +903,7 @@ act_dump_config() { void set_keycmd() { - run_keycmd(FALSE, uzbl.state.keycmd); + run_keycmd(FALSE); update_title(); } @@ -1181,7 +1176,7 @@ keycmd (WebKitWebView *page, GArray *argv, GString *result) { (void)argv; (void)result; uzbl.state.keycmd = g_strdup(argv_idx(argv, 0)); - run_keycmd(FALSE, uzbl.state.keycmd); + run_keycmd(FALSE); update_title(); } @@ -1191,7 +1186,7 @@ keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) { (void)argv; (void)result; uzbl.state.keycmd = g_strdup(argv_idx(argv, 0)); - run_keycmd(TRUE, uzbl.state.keycmd); + run_keycmd(TRUE); update_title(); } @@ -2075,7 +2070,7 @@ key_press_cb (GtkWidget* window, GdkEventKey* event) return FALSE; if (event->keyval == GDK_Escape) { - clear_keycmd(&uzbl.state.keycmd); + clear_keycmd(); update_title(); dehilight(uzbl.gui.web_view, NULL, NULL); return TRUE; @@ -2111,49 +2106,144 @@ key_press_cb (GtkWidget* window, GdkEventKey* event) uzbl.state.keycmd = g_string_free(keycmd, FALSE); } - run_keycmd(key_ret, uzbl.state.keycmd); + run_keycmd(key_ret); update_title(); if (key_ret) return (!uzbl.behave.insert_mode); return TRUE; } +/* Handler for mouse button events NOT within the webkit frame. These + * are represented with binds like the following: + * + * bind = set uri = http://www.example.com/ + * + * which would navigate to Example, whenever the middle-button was + * clicked on the status bar. + * + * The following should work (depending on your configuration): + * through to + */ gboolean mouse_button_cb (GtkWidget* window, GdkEventButton* event) { - gchar* mousecmd = g_strdup(""); + gchar* mousecmd = NULL; (void) window; if (event->type != GDK_BUTTON_PRESS || event->button >= 10) return FALSE; - /* mousecmd = "" */ - *(mousecmd+6) = '0' + event->button; + /* Status (and non-webkit bits) click */ + + mousecmd = g_strdup(""); + *(mousecmd+13) = '0' + event->button; if (uzbl.state.verbose) printf("Mouse Button: %s\n", mousecmd); + /* Look for binding, and execute if found */ if (g_hash_table_lookup(uzbl.bindings, mousecmd)) { - run_keycmd(FALSE, mousecmd); + uzbl.state.keycmd = g_strdup(mousecmd); + g_free(mousecmd); + run_keycmd(FALSE); return TRUE; } + /* No binds found */ + g_free(mousecmd); return FALSE; } +/* Handler for mouse button events within the webkit frame. To bind + * a click of a mouse button on any area of the page (other than links): + * + * bind = set uri = http://www.example.com/ + * + * which would navigate to Example, whenever the middle-button was + * clicked on the page body. You can bind for clicking on links: + * + * bind _ = spawn uzbl -u %s + * + * With the above, when a link is middle-clicked, a new uzbl process + * would be spawned, opening the URL of the link you clicked. + * + * The following should work (depending on your configuration): + * through to , and + * through to + * + * In order to use the link URI in binds, define thus: + * bind _ = set uri = %s + */ gboolean wk_mouse_button_cb (WebKitWebView *web_view, GdkEventButton* event) { + gchar* mousecmd = NULL; + (void) web_view; - return mouse_button_cb(NULL, event); + if (event->type != GDK_BUTTON_PRESS || event->button >= 10) + return FALSE; + + mousecmd = g_strdup("<"); + + if (uzbl.state.selected_url) { + /* It's a link click */ + + /* In order to use the URL, we are specify the "_" in the bind */ + mousecmd = g_strconcat(mousecmd, "link-mouseX>_", NULL); + *(mousecmd+11) = '0' + event->button; + + /* Check for bind (with _) */ + if (g_hash_table_lookup(uzbl.bindings, mousecmd)) { + /* If found, call the binding with the hovered over URI as the + * parameter. + */ + mousecmd[strlen(mousecmd) - 1] = '\0'; + uzbl.state.keycmd = g_strconcat(mousecmd, uzbl.state.selected_url, NULL); + g_free(mousecmd); + run_keycmd(TRUE); + return TRUE; + } + + /* Get rid of '_' so we can check for a bind without it below */ + mousecmd[strlen(mousecmd) - 1] = '\0'; + + } else { + /* Body click */ + + mousecmd = g_strconcat(mousecmd, "mouseX>", NULL); + *(mousecmd+6) = '0' + event->button; + } + + if (uzbl.state.verbose) + printf("Mouse Button: %s\n", mousecmd); + + /* Look for binding, and execute if found */ + if (g_hash_table_lookup(uzbl.bindings, mousecmd)) { + uzbl.state.keycmd = g_strdup(mousecmd); + g_free(mousecmd); + run_keycmd(FALSE); + return TRUE; + } + + /* No binds found */ + + g_free(mousecmd); + return FALSE; } +/* Handler for mouse scroll events NOT within the webkit frame. Works + * the same as mouse_button_cb, except the names are: + * , , , and + * + */ gboolean mouse_scroll_cb (GtkWidget* window, GdkEventScroll* event) { - gchar* mousecmd = g_strdup(", , , , , + * , and + * + * In order to use the link URI in binds, define thus: + * bind _ = set uri = %s + */ gboolean wk_mouse_scroll_cb (WebKitWebView *web_view, GdkEventScroll* event) { + gchar* mousecmd = g_strdup("<"); + (void) web_view; - return mouse_scroll_cb(NULL, event); + if (uzbl.state.selected_url) { + /* It's a link click */ + + mousecmd = g_strconcat(mousecmd, "link-", NULL); + } + + mousecmd = g_strconcat(mousecmd, "wheel", NULL); + + switch (event->direction) { + case GDK_SCROLL_UP: + mousecmd = g_strconcat(mousecmd, "up>", NULL); + break; + case GDK_SCROLL_DOWN: + mousecmd = g_strconcat(mousecmd, "down>", NULL); + break; + + case GDK_SCROLL_LEFT: + mousecmd = g_strconcat(mousecmd, "left>", NULL); + break; + + case GDK_SCROLL_RIGHT: + mousecmd = g_strconcat(mousecmd, "right>", NULL); + break; + + default: + g_free(mousecmd); + return FALSE; + } + + if (uzbl.state.verbose) + printf("Mouse Scroll: %s\n", mousecmd); + + if (uzbl.state.selected_url) { + /* It's a link click */ + + mousecmd = g_strconcat(mousecmd, "_", NULL); + + /* Check for binding with '_' */ + if (g_hash_table_lookup(uzbl.bindings, mousecmd)) { + mousecmd[strlen(mousecmd) - 1] = '\0'; + uzbl.state.keycmd = g_strconcat(mousecmd, uzbl.state.selected_url, NULL); + g_free(mousecmd); + run_keycmd(TRUE); + return TRUE; + } + + /* Get rid of '_' so we can check for a bind without it below */ + mousecmd[strlen(mousecmd) - 1] = '\0'; + } + + /* Look for binding, and execute if found */ + if (g_hash_table_lookup(uzbl.bindings, mousecmd)) { + uzbl.state.keycmd = g_strdup(mousecmd); + g_free(mousecmd); + run_keycmd(FALSE); + return TRUE; + } + + /* No binds found */ + + g_free(mousecmd); + return FALSE; } void -run_keycmd(const gboolean key_ret, gchar* keycmd) { +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, keycmd))) { - clear_keycmd(&keycmd); + if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) { + clear_keycmd(); parse_command(act->name, act->param, NULL); return; } @@ -2212,9 +2378,9 @@ run_keycmd(const gboolean key_ret, gchar* keycmd) { GString* short_keys = g_string_new (""); GString* short_keys_inc = g_string_new (""); guint i; - guint len = strlen(keycmd); + guint len = strlen(uzbl.state.keycmd); for (i=0; istr); g_string_append_c(short_keys, '_'); g_string_append_c(short_keys_inc, '*'); @@ -2222,11 +2388,11 @@ run_keycmd(const gboolean key_ret, gchar* keycmd) { 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(&keycmd); + 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(&keycmd); + clear_keycmd(); else exec_paramcmd(act, i); /* otherwise execute the incremental */ break; } @@ -2298,6 +2464,10 @@ create_mainbar () { 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); + /* These are here and not in create_window as webkit sometimes passes + * events through to the window if it doesn't deal with them. This isn't + * desired behaviour in this case. + */ g_signal_connect (G_OBJECT (g->mainbar), "button-press-event", G_CALLBACK (mouse_button_cb), NULL); g_signal_connect (G_OBJECT (g->mainbar), "scroll-event", G_CALLBACK (mouse_scroll_cb), NULL); return g->mainbar; @@ -2311,8 +2481,6 @@ create_window () { 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); - g_signal_connect (G_OBJECT (window), "button-press-event", G_CALLBACK (mouse_button_cb), NULL); - g_signal_connect (G_OBJECT (window), "scroll-event", G_CALLBACK (mouse_scroll_cb), NULL); return window; } diff --git a/uzbl.h b/uzbl.h index 650f960..17235be 100644 --- a/uzbl.h +++ b/uzbl.h @@ -398,7 +398,7 @@ gboolean wk_mouse_scroll_cb (WebKitWebView *web_view, GdkEventScroll* event); void -run_keycmd(const gboolean key_ret, gchar* keycmd); +run_keycmd(const gboolean key_ret); void exec_paramcmd(const Action* act, const guint i); -- cgit v1.2.3 From 9efb0d3005f06e7e75b45d9380db2bbebd8a5729 Mon Sep 17 00:00:00 2001 From: Todd Harbour Date: Tue, 21 Jul 2009 04:22:38 +1000 Subject: Added -x/--exec cmd option to exec uzbl commands --- README | 1 + uzbl.c | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README b/README index d73bab1..bd16503 100644 --- a/README +++ b/README @@ -406,6 +406,7 @@ Copying the Uzbl object and creating public functions should be taken with care -v, --verbose Whether to print all messages or just errors. -n, --name=NAME Name of the current instance (defaults to Xorg window id) -c, --config=FILE Path to config file or '-' for stdin + -x, --execute=COMMAND Command to execute -s, --socket=SOCKET Socket ID -g, --geometry=GEOMETRY Set window geometry (format: WIDTHxHEIGHT+-X+-Y) -V, --version Print the version and exit diff --git a/uzbl.c b/uzbl.c index f5f35d9..ed45973 100644 --- a/uzbl.c +++ b/uzbl.c @@ -71,6 +71,8 @@ GOptionEntry entries[] = "Name of the current instance (defaults to Xorg window id)", "NAME" }, { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Path to config file or '-' for stdin", "FILE" }, + { "execute", 'x', 0, G_OPTION_ARG_STRING, &uzbl.state.keycmd, + "Command to execute after starting", "COMMAND" }, { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id, "Socket ID", "SOCKET" }, { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry, @@ -2903,7 +2905,9 @@ initialize(int argc, char *argv[]) { 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(""); + + /* If it hasn't been provided on the cmdline */ + if (!uzbl.state.keycmd) uzbl.state.keycmd = g_strdup(""); if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR) fprintf(stderr, "uzbl: error hooking SIGTERM\n"); @@ -3018,6 +3022,18 @@ main (int argc, char* argv[]) { if (verbose_override > uzbl.state.verbose) uzbl.state.verbose = verbose_override; + /* Whilst a command isn't a keycmd, using the variable, avoids the + * need for a custom one. Command was chosen instead of keycmd as + * an option because keycmd is easier to emulate (using keycmd_nl) + * than the other way around. + */ + if (strlen(uzbl.state.keycmd) > 0) { + GArray *cmd = g_array_new(TRUE, FALSE, sizeof(gchar*)); + g_array_append_val(cmd, uzbl.state.keycmd); + chain(uzbl.gui.web_view, cmd, NULL); + g_array_free(cmd, TRUE); + } + if (uri_override) { set_var_value("uri", uri_override); g_free(uri_override); -- cgit v1.2.3 From 64a82ece8d447972061d98fb3af6fa8a853a912d Mon Sep 17 00:00:00 2001 From: Todd Harbour Date: Tue, 21 Jul 2009 04:50:37 +1000 Subject: Revert "Added -x/--exec cmd option to exec uzbl commands" This reverts commit 9efb0d3005f06e7e75b45d9380db2bbebd8a5729. stdin already functions like this anyway :/ --- README | 1 - uzbl.c | 18 +----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/README b/README index bd16503..d73bab1 100644 --- a/README +++ b/README @@ -406,7 +406,6 @@ Copying the Uzbl object and creating public functions should be taken with care -v, --verbose Whether to print all messages or just errors. -n, --name=NAME Name of the current instance (defaults to Xorg window id) -c, --config=FILE Path to config file or '-' for stdin - -x, --execute=COMMAND Command to execute -s, --socket=SOCKET Socket ID -g, --geometry=GEOMETRY Set window geometry (format: WIDTHxHEIGHT+-X+-Y) -V, --version Print the version and exit diff --git a/uzbl.c b/uzbl.c index ed45973..f5f35d9 100644 --- a/uzbl.c +++ b/uzbl.c @@ -71,8 +71,6 @@ GOptionEntry entries[] = "Name of the current instance (defaults to Xorg window id)", "NAME" }, { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Path to config file or '-' for stdin", "FILE" }, - { "execute", 'x', 0, G_OPTION_ARG_STRING, &uzbl.state.keycmd, - "Command to execute after starting", "COMMAND" }, { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id, "Socket ID", "SOCKET" }, { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry, @@ -2905,9 +2903,7 @@ initialize(int argc, char *argv[]) { 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(); - - /* If it hasn't been provided on the cmdline */ - if (!uzbl.state.keycmd) uzbl.state.keycmd = g_strdup(""); + uzbl.state.keycmd = g_strdup(""); if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR) fprintf(stderr, "uzbl: error hooking SIGTERM\n"); @@ -3022,18 +3018,6 @@ main (int argc, char* argv[]) { if (verbose_override > uzbl.state.verbose) uzbl.state.verbose = verbose_override; - /* Whilst a command isn't a keycmd, using the variable, avoids the - * need for a custom one. Command was chosen instead of keycmd as - * an option because keycmd is easier to emulate (using keycmd_nl) - * than the other way around. - */ - if (strlen(uzbl.state.keycmd) > 0) { - GArray *cmd = g_array_new(TRUE, FALSE, sizeof(gchar*)); - g_array_append_val(cmd, uzbl.state.keycmd); - chain(uzbl.gui.web_view, cmd, NULL); - g_array_free(cmd, TRUE); - } - if (uri_override) { set_var_value("uri", uri_override); g_free(uri_override); -- cgit v1.2.3 From a95a7a4cb45b4bf1b24f4dc4acba6fecde1dd80b Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Tue, 23 Mar 2010 22:06:43 +0100 Subject: add my e-mail to AUTHORS file --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index aa56e17..ca484f6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -54,7 +54,7 @@ In alphabetical order: Nicolas Pouillard - refactored scroll command Olivier Schwander - auto file:// prepend Paul Tomak - eFormFiller - Paweł Zuzelski (pawelz) - http auth handler, misc patches + Paweł Zuzelski (pawelz) - http auth handler, misc patches Peter Suschlik - backwards searching Přemysl Hrubý (anydot) - several C contributions and cleanups Robert Manea (robm) - C code all over the place -- cgit v1.2.3 From 1dc6b9e5646542180490ae2e92eff304564fc8fc Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Fri, 26 Mar 2010 17:52:36 +0100 Subject: checkbox support in 'once' action --- examples/data/scripts/formfiller.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 1408006..5dc6345 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -101,7 +101,7 @@ then sed 's/]\+>\).*/\1/;/type="\(password\|text\)"/Ip' | \ + sed -n 's/.*\(]\+>\).*/\1/;/type="\(password\|text\|checkbox\)"/Ip' | \ sed 's/\(.*\)\(type="[^"]\+"\)\(.*\)\(name="[^"]\+"\)\(.*\)/\1\4\3\2\5/I' | \ sed 's/.*name="\([^"]\+\)".*type="\([^"]\+\)".*/\1(\2):/I' >> $tmpfile echo "${html}" | \ @@ -111,7 +111,11 @@ then [ -e $tmpfile ] || exit 2 cat $tmpfile | \ - sed -n -e 's/\([^(]\+\)([^)]\+):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].value="\2" } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\2"/p' | \ + sed -n -e 's/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].value="\3" } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\3"/p' | \ + sed -e 's/@/\\@/g' >> $fifo + sed 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/\1(\2):1/;s/\([^(]\+\)(\(checkbox\)):$/\1(\2):0/' -i $tmpfile + cat $tmpfile | \ + sed -n -e 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].checked=\3 } } catch(err) { } } }; document.getElementsByName("\1")[0].checked=\3/p' | \ sed -e 's/@/\\@/g' >> $fifo rm -f $tmpfile else -- cgit v1.2.3 From f18b80263ad72f432d9127f3025ea85bf89e87c7 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Fri, 26 Mar 2010 20:05:48 +0100 Subject: Speed up for forms output to the file Moved dumping javascript code to the function dump() outerHTML is now being used only on forms, not entire documents --- examples/data/scripts/formfiller.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 5dc6345..4b62ea1 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -74,6 +74,8 @@ then action="new" fi +dumpFunction='function dump() { var output = ""; var allFrames = new Array(window); for(f=0;f 0) { for(k=0;k 0) { for(j=0;j 0) { for(i=0;i Date: Sat, 27 Mar 2010 01:08:52 +0100 Subject: updated formfiller.sh comments --- examples/data/scripts/formfiller.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 4b62ea1..20d1d52 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -6,13 +6,14 @@ # files contain lines like: !profile= # (fieldtype): # profile_name should be replaced with a name that will tell sth about that profile -# fieldtype can be text or password - only for information pupropse (auto-generated) - don't change that +# fieldtype can be checkbox, text or password - only for information pupropse (auto-generated) - don't change that # # user arg 1: # edit: force editing the file (falls back to new if not found) # new: start with a new file. # load: try to load from file into form # add: try to add another profile to an existing file +# once: edit form using external editor # # something else (or empty): if file not available: new, otherwise load. -- cgit v1.2.3 From 32a4061abdd5f30e4a25c982fb52b09c08b39fb5 Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Sat, 27 Mar 2010 01:14:34 +0100 Subject: quote ${editor} and ${VISUAL} These variables must be quoted. Otherwise script fails for VISUAL="xterm -name xvim -e vim" --- examples/data/scripts/formfiller.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 20d1d52..6f6dc34 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -37,9 +37,9 @@ keydir=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/dforms [ -d "`dirname $keydir`" ] || exit 1 [ -d "$keydir" ] || mkdir "$keydir" -editor=${VISUAL} -if [ -z ${editor} ]; then - if [ -z ${EDITOR} ]; then +editor="${VISUAL}" +if [ -z "${editor}" ]; then + if [ -z "${EDITOR}" ]; then editor='xterm -e vim' else editor="xterm -e ${EDITOR}" -- cgit v1.2.3 From fc50da64fe96da436896aa077ee8a8f807969220 Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Sat, 27 Mar 2010 01:37:42 +0100 Subject: Use javascript and XPath to find input fields. Initialize field values. --- examples/data/scripts/formfiller-helper.js | 32 ++++++++++++++++++++++++++++++ examples/data/scripts/formfiller.sh | 17 ++-------------- 2 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 examples/data/scripts/formfiller-helper.js diff --git a/examples/data/scripts/formfiller-helper.js b/examples/data/scripts/formfiller-helper.js new file mode 100644 index 0000000..999d860 --- /dev/null +++ b/examples/data/scripts/formfiller-helper.js @@ -0,0 +1,32 @@ +/* This script finds all editable input elemnts and generate file for + * formfiller script. It must be invoked from formfiller.sh */ + +(function () { + /* evaluate XPath query */ + var xp_res=document.evaluate("//input", document.documentElement, null, XPathResult.ANY_TYPE,null); + var rv=""; + var input; + + while(input=xp_res.iterateNext()) { + var type=(input.type?input.type:text); + switch (type) { + case "text": + case "password": + case "file": + rv += input.name + "(" + type + "):" + input.value + "\n"; + break; + case "checkbox": + case "radio": + rv += input.name + "[" + input.value + "]" + "(" + type + "):" + (input.checked?"ON":"") + "\n"; + break; + /* Not supported: + * case "button": + * case "image": + * case "reset": + * case "submit": + * case "hidden": + */ + } + } + return rv; +})() diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 6f6dc34..9fbe8fc 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -75,8 +75,6 @@ then action="new" fi -dumpFunction='function dump() { var output = ""; var allFrames = new Array(window); for(f=0;f 0) { for(k=0;k/>\n/g' | \ - sed 's/]\+>\).*/\1/;/type="\(password\|text\|checkbox\)"/Ip' | \ - sed 's/\(.*\)\(type="[^"]\+"\)\(.*\)\(name="[^"]\+"\)\(.*\)/\1\4\3\2\5/I' | \ - sed 's/.*name="\([^"]\+\)".*type="\([^"]\+\)".*/\1(\2):/I' >> $tmpfile - echo "${html}" | \ - sed -n 's/.*> $tmpfile + echo "script @scripts_dir/formfiller-helper.js" | \ + socat - unix-connect:$socket > $tmpfile ${editor} $tmpfile [ -e $tmpfile ] || exit 2 -- cgit v1.2.3 From 3dab456619fc0fc306dbb08faafbb18aef0e72e2 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sat, 27 Mar 2010 07:57:26 +0100 Subject: Moved function from formfiller-helper.js into the formfiller.sh --- examples/data/scripts/formfiller-helper.js | 32 ------------------------------ examples/data/scripts/formfiller.sh | 18 ++++++++++++++++- 2 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 examples/data/scripts/formfiller-helper.js diff --git a/examples/data/scripts/formfiller-helper.js b/examples/data/scripts/formfiller-helper.js deleted file mode 100644 index 999d860..0000000 --- a/examples/data/scripts/formfiller-helper.js +++ /dev/null @@ -1,32 +0,0 @@ -/* This script finds all editable input elemnts and generate file for - * formfiller script. It must be invoked from formfiller.sh */ - -(function () { - /* evaluate XPath query */ - var xp_res=document.evaluate("//input", document.documentElement, null, XPathResult.ANY_TYPE,null); - var rv=""; - var input; - - while(input=xp_res.iterateNext()) { - var type=(input.type?input.type:text); - switch (type) { - case "text": - case "password": - case "file": - rv += input.name + "(" + type + "):" + input.value + "\n"; - break; - case "checkbox": - case "radio": - rv += input.name + "[" + input.value + "]" + "(" + type + "):" + (input.checked?"ON":"") + "\n"; - break; - /* Not supported: - * case "button": - * case "image": - * case "reset": - * case "submit": - * case "hidden": - */ - } - } - return rv; -})() diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 9fbe8fc..c00d924 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -75,6 +75,22 @@ then action="new" fi +dumpFunction='function dump() { + var rv=""; + var xp_res=document.evaluate("//input", document.documentElement, null, XPathResult.ANY_TYPE,null); + var input; + while(input=xp_res.iterateNext()) { + var type=(input.type?input.type:text); + if(type == "text" || type == "password" || type == "file") { + rv += input.name + "(" + type + "):" + input.value + "\\n"; + } + else if(type == "checkbox" || type == "radio") { + rv += input.name + "[" + input.value + "]" + "(" + type + "):" + (input.checked?"ON":"") + "\\n"; + } + } + return rv; +}; ' + if [ "$action" = 'load' ] then [ -e $keydir/$domain ] || exit 2 @@ -92,7 +108,7 @@ then elif [ "$action" = "once" ] then tmpfile=`mktemp` - echo "script @scripts_dir/formfiller-helper.js" | \ + echo "js "$dumpFunction" dump(); " | \ socat - unix-connect:$socket > $tmpfile ${editor} $tmpfile -- cgit v1.2.3 From 949997d22d899e8f70cfca7631d0e273f7623854 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sat, 27 Mar 2010 08:33:13 +0100 Subject: Forms dumping function remake to work with all frames and iframes --- examples/data/scripts/formfiller.sh | 47 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index c00d924..a529ca0 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -76,18 +76,27 @@ then fi dumpFunction='function dump() { - var rv=""; - var xp_res=document.evaluate("//input", document.documentElement, null, XPathResult.ANY_TYPE,null); - var input; - while(input=xp_res.iterateNext()) { - var type=(input.type?input.type:text); - if(type == "text" || type == "password" || type == "file") { - rv += input.name + "(" + type + "):" + input.value + "\\n"; - } - else if(type == "checkbox" || type == "radio") { - rv += input.name + "[" + input.value + "]" + "(" + type + "):" + (input.checked?"ON":"") + "\\n"; - } + var rv=""; + var allFrames = new Array(window); + for(f=0;f $tmpfile + socat - unix-connect:$socket | \ + sed -n '/^[^(]\+([^)]\+):/p' > $tmpfile ${editor} $tmpfile [ -e $tmpfile ] || exit 2 @@ -147,16 +157,9 @@ else # login(text): # passwd(password): # - html=`echo 'js '${dumpFunction}' dump(); ' | \ - socat - unix-connect:$socket` - echo ${html} | \ - tr -d '\n' | \ - sed 's/>/>\n/g' | \ - sed 's/]\+>\).*/\1/;/type="\(password\|text\)"/Ip' | \ - sed 's/\(.*\)\(type="[^"]\+"\)\(.*\)\(name="[^"]\+"\)\(.*\)/\1\4\3\2\5/I' | \ - sed 's/.*name="\([^"]\+\)".*type="\([^"]\+\)".*/\1(\2):/I' >> $keydir/$domain + echo "js "$dumpFunction" dump(); " | \ + socat - unix-connect:$socket | \ + sed -n '/^[^(]\+([^)]\+):/p' > $keydir/$domain fi [ -e "$keydir/$domain" ] || exit 3 #this should never happen, but you never know. $editor "$keydir/$domain" #TODO: if user aborts save in editor, the file is already overwritten -- cgit v1.2.3 From f9cbf6cb3eb1e292e4b8e8fb4d9e8c00c5545ad6 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sat, 27 Mar 2010 08:35:16 +0100 Subject: Formfiller.sh comments update --- examples/data/scripts/formfiller.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index a529ca0..afce151 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -6,7 +6,8 @@ # files contain lines like: !profile= # (fieldtype): # profile_name should be replaced with a name that will tell sth about that profile -# fieldtype can be checkbox, text or password - only for information pupropse (auto-generated) - don't change that +# fieldtype can be checkbox, text or password, textarea - only for information +# pupropse (auto-generated) - don't change that # # user arg 1: # edit: force editing the file (falls back to new if not found) @@ -16,6 +17,9 @@ # once: edit form using external editor # # something else (or empty): if file not available: new, otherwise load. +# +# TODO: +# - Add multiline support for textarea fields # config dmenu colors and prompt NB="#0f0f0f" @@ -124,10 +128,10 @@ then [ -e $tmpfile ] || exit 2 + sed 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/\1(\2):1/;s/\([^(]\+\)(\(checkbox\)):$/\1(\2):0/' -i $tmpfile cat $tmpfile | \ sed -n -e 's/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].value="\3" } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\3"/p' | \ sed -e 's/@/\\@/g' >> $fifo - sed 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/\1(\2):1/;s/\([^(]\+\)(\(checkbox\)):$/\1(\2):0/' -i $tmpfile cat $tmpfile | \ sed -n -e 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].checked=\3 } } catch(err) { } } }; document.getElementsByName("\1")[0].checked=\3/p' | \ sed -e 's/@/\\@/g' >> $fifo -- cgit v1.2.3 From 6c427ad053b8542e17285ce62ed2b7dde0966472 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sat, 27 Mar 2010 08:45:52 +0100 Subject: Checkbox and textarea support also for remembered forms --- examples/data/scripts/formfiller.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index afce151..7bb7cae 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -114,9 +114,13 @@ then option=`echo -e -n "$menu"| dmenu ${LINES} -nb "${NB}" -nf "${NF}" -sb "${SB}" -sf "${SF}" -p "${PROMPT}"` fi + sed 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/\1(\2):1/;s/\([^(]\+\)(\(checkbox\)):$/\1(\2):0/' -i $keydir/$domain cat $keydir/$domain | \ sed -n -e "/^!profile=${option}/,/^!profile=/p" | \ - sed -n -e 's/\([^(]\+\)([^)]\+):[ ]*\(.\+\)$/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].value="\2"; } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\2"/p' | \ + sed -n -e 's/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].value="\3" } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\3"/p' | \ + sed -e 's/@/\\@/g' >> $fifo + cat $keydir/$domain | \ + sed -n -e 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].checked=\3 } } catch(err) { } } }; document.getElementsByName("\1")[0].checked=\3/p' | \ sed -e 's/@/\\@/g' >> $fifo elif [ "$action" = "once" ] then @@ -163,7 +167,7 @@ else # echo "js "$dumpFunction" dump(); " | \ socat - unix-connect:$socket | \ - sed -n '/^[^(]\+([^)]\+):/p' > $keydir/$domain + sed -n '/^[^(]\+([^)]\+):/p' >> $keydir/$domain fi [ -e "$keydir/$domain" ] || exit 3 #this should never happen, but you never know. $editor "$keydir/$domain" #TODO: if user aborts save in editor, the file is already overwritten -- cgit v1.2.3 From 60a692daa915c1708b159216b1d5b2bed1667c4d Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sun, 28 Mar 2010 09:43:13 +0200 Subject: Added support for radio-buttons. Little code reorganisation --- examples/data/scripts/formfiller.sh | 60 +++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 7bb7cae..a1f7392 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -95,7 +95,7 @@ dumpFunction='function dump() { rv += input.name + "(" + type + "):" + input.value + "\\n"; } else if(type == "checkbox" || type == "radio") { - rv += input.name + "(" + type + "):" + (input.checked?"ON":"") + "\\n"; + rv += input.name + "[" + input.value + "](" + type + "):" + (input.checked?"ON":"") + "\\n"; } } } @@ -104,6 +104,32 @@ dumpFunction='function dump() { return rv; }; ' +insertFunction="function insert(fname, ftype, fvalue, fchecked) { \ + var allFrames = new Array(window); \ + for(f=0;f 0) { for(i=0;i 0) { e[0].value="\3" } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\3"/p' | \ - sed -e 's/@/\\@/g' >> $fifo - cat $keydir/$domain | \ - sed -n -e 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].checked=\3 } } catch(err) { } } }; document.getElementsByName("\1")[0].checked=\3/p' | \ - sed -e 's/@/\\@/g' >> $fifo + sed 's/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/\1[\2](\3):1/;s/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):$/\1[\2](\3):0/' -i $keydir/$domain + fields=`cat $keydir/$domain | \ + sed -n -e "/^!profile=${option}/,/^!profile=/p"` + echo "${fields}" | \ + sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ + sed -e 's/@/\\@/g' > $fifo + echo "${fields}" | \ + sed -n -e "s/\([^[]\+\)\[\([^]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', '\4');/p" | \ + sed -e 's/@/\\@/g' > $fifo elif [ "$action" = "once" ] then tmpfile=`mktemp` @@ -132,13 +159,14 @@ then [ -e $tmpfile ] || exit 2 - sed 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/\1(\2):1/;s/\([^(]\+\)(\(checkbox\)):$/\1(\2):0/' -i $tmpfile - cat $tmpfile | \ - sed -n -e 's/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].value="\3" } } catch(err) { } } }; document.getElementsByName("\1")[0].value="\3"/p' | \ - sed -e 's/@/\\@/g' >> $fifo - cat $tmpfile | \ - sed -n -e 's/\([^(]\+\)(\(checkbox\)):[ ]*\(.\+\)/js if(window.frames.length > 0) { for(i=0;i 0) { e[0].checked=\3 } } catch(err) { } } }; document.getElementsByName("\1")[0].checked=\3/p' | \ - sed -e 's/@/\\@/g' >> $fifo + sed 's/\([^[]\+\)\[\([^]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/\1[\2](\3):1/;s/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):$/\1[\2](\3):0/' -i $tmpfile + fields=`cat $tmpfile` + echo "${fields}" | \ + sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ + sed -e 's/@/\\@/g' > $fifo + echo "${fields}" | \ + sed -n -e "s/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', '\4');/p" | \ + sed -e 's/@/\\@/g' > $fifo rm -f $tmpfile else if [ "$action" == 'new' -o "$action" == 'add' ] -- cgit v1.2.3 From 0331e7be9ff08e289630e1d2a7b1c1f4da8f3f06 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sun, 28 Mar 2010 10:43:35 +0200 Subject: Added support for multilines in textareas --- examples/data/scripts/formfiller.sh | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index a1f7392..4204dc8 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -95,9 +95,14 @@ dumpFunction='function dump() { rv += input.name + "(" + type + "):" + input.value + "\\n"; } else if(type == "checkbox" || type == "radio") { - rv += input.name + "[" + input.value + "](" + type + "):" + (input.checked?"ON":"") + "\\n"; + rv += input.name + "[" + input.value + "](" + type + "):" + (input.checked?"ON":"OFF") + "\\n"; } } + xp_res=allFrames[j].document.evaluate("//textarea", allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null); + var input; + while(input=xp_res.iterateNext()) { + rv += input.name + "(textarea):" + input.value + "\\n"; + } } catch(err) { } } @@ -140,14 +145,17 @@ then option=`echo -e -n "$menu"| dmenu ${LINES} -nb "${NB}" -nf "${NF}" -sb "${SB}" -sf "${SF}" -p "${PROMPT}"` fi - sed 's/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/\1[\2](\3):1/;s/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):$/\1[\2](\3):0/' -i $keydir/$domain + sed 's/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(on\|yes\|1\)$/\1[\2](\3):1/I;s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(off\|no\|0\)$/\1[\2](\3):0/I' -i $keydir/$domain fields=`cat $keydir/$domain | \ - sed -n -e "/^!profile=${option}/,/^!profile=/p"` + sed -n -e "/^!profile=${option}/,/^!profile=/p" | \ + sed 's/^\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)/<\1>/' | \ + sed 's/^\(.\+\)$/\1{{br}}/g' | \ + tr -d '\n'| sed 's/{{br}}<\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)>/\n\1/g'` echo "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ - sed -e 's/@/\\@/g' > $fifo + sed -e 's/@/\\@/g;s/{{br}}/\\\\n/g' > $fifo echo "${fields}" | \ - sed -n -e "s/\([^[]\+\)\[\([^]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', '\4');/p" | \ + sed -n -e "s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ sed -e 's/@/\\@/g' > $fifo elif [ "$action" = "once" ] then @@ -159,13 +167,16 @@ then [ -e $tmpfile ] || exit 2 - sed 's/\([^[]\+\)\[\([^]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/\1[\2](\3):1/;s/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):$/\1[\2](\3):0/' -i $tmpfile - fields=`cat $tmpfile` + sed 's/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(on\|yes\|1\)$/\1[\2](\3):1/I;s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(off\|no\|0\)$/\1[\2](\3):0/I' -i $tmpfile + fields=`cat $tmpfile | \ + sed 's/^\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)/<\1>/' | \ + sed 's/^\(.\+\)$/\1{{br}}/g' | \ + tr -d '\n'| sed 's/{{br}}<\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)>/\n\1/g'` echo "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ - sed -e 's/@/\\@/g' > $fifo + sed -e 's/@/\\@/g;s/{{br}}/\\\\n/g' > $fifo echo "${fields}" | \ - sed -n -e "s/\([^\[]\+\)\[\([^\]]*\)](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', '\4');/p" | \ + sed -n -e "s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ sed -e 's/@/\\@/g' > $fifo rm -f $tmpfile else -- cgit v1.2.3 From b3f4920cb4ec57f60112d1dd28e33b2b1260b9a4 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sun, 28 Mar 2010 10:52:20 +0200 Subject: Added some comments about textareas --- examples/data/scripts/formfiller.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 4204dc8..8034fba 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -5,10 +5,18 @@ # uses settings files like: $keydir/ # files contain lines like: !profile= # (fieldtype): -# profile_name should be replaced with a name that will tell sth about that profile +# profile_name should be replaced with a name that will tell sth about that +# profile # fieldtype can be checkbox, text or password, textarea - only for information # pupropse (auto-generated) - don't change that # +# Texteares: for textareas edited text can be now splitted into more lines. +# If there will be text, that doesn't match key line: +# (fieldtype): +# then it will be considered as a multiline for the first field above it +# If that field doesn't support multilines, like all input fields, then +# only first line will be inserted +# # user arg 1: # edit: force editing the file (falls back to new if not found) # new: start with a new file. @@ -18,8 +26,6 @@ # # something else (or empty): if file not available: new, otherwise load. # -# TODO: -# - Add multiline support for textarea fields # config dmenu colors and prompt NB="#0f0f0f" -- cgit v1.2.3 From 12897363d6c7d6a9cab03266a16be16eddab7ed0 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sun, 28 Mar 2010 10:59:13 +0200 Subject: Comments fixup --- examples/data/scripts/formfiller.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 8034fba..cc7d900 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -14,8 +14,8 @@ # If there will be text, that doesn't match key line: # (fieldtype): # then it will be considered as a multiline for the first field above it -# If that field doesn't support multilines, like all input fields, then -# only first line will be inserted +# Keep in mind, that if you make more than one line for fileds like input +# text fields, then all lines will be inserted into as one line # # user arg 1: # edit: force editing the file (falls back to new if not found) -- cgit v1.2.3 From 4792e67bd0fb1fbdb224d617184f1d54eddaecd5 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sun, 28 Mar 2010 17:56:05 +0200 Subject: Fixed some issues connected with multilines for textareas --- examples/data/scripts/formfiller.sh | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index cc7d900..5eab784 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -97,11 +97,11 @@ dumpFunction='function dump() { var input; while(input=xp_res.iterateNext()) { var type=(input.type?input.type:text); - if(type == "text" || type == "password" || type == "file") { + if(type == "text" || type == "password") { rv += input.name + "(" + type + "):" + input.value + "\\n"; } else if(type == "checkbox" || type == "radio") { - rv += input.name + "[" + input.value + "](" + type + "):" + (input.checked?"ON":"OFF") + "\\n"; + rv += input.name + "{" + input.value + "}(" + type + "):" + (input.checked?"ON":"OFF") + "\\n"; } } xp_res=allFrames[j].document.evaluate("//textarea", allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null); @@ -151,17 +151,19 @@ then option=`echo -e -n "$menu"| dmenu ${LINES} -nb "${NB}" -nf "${NF}" -sb "${SB}" -sf "${SF}" -p "${PROMPT}"` fi - sed 's/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(on\|yes\|1\)$/\1[\2](\3):1/I;s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(off\|no\|0\)$/\1[\2](\3):0/I' -i $keydir/$domain + sed 's/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):.\+$/\1{\2}(\3):1/I;s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):$/\1{\2}(\3):0/I' -i $keydir/$domain fields=`cat $keydir/$domain | \ - sed -n -e "/^!profile=${option}/,/^!profile=/p" | \ - sed 's/^\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)/<\1>/' | \ - sed 's/^\(.\+\)$/\1{{br}}/g' | \ - tr -d '\n'| sed 's/{{br}}<\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)>/\n\1/g'` + sed -n "/^!profile=${option}/,/^!profile=/p" | \ + sed '/^!profile=/d' | \ + sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):/%{>\1\2):<}%/' | \ + sed 's/^\(.\+\)$/<{br}>\1/' | \ + tr -d '\n' | \ + sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):<}%/\\n\1\2):/g'` echo "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ - sed -e 's/@/\\@/g;s/{{br}}/\\\\n/g' > $fifo + sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo echo "${fields}" | \ - sed -n -e "s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ + sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ sed -e 's/@/\\@/g' > $fifo elif [ "$action" = "once" ] then @@ -173,16 +175,17 @@ then [ -e $tmpfile ] || exit 2 - sed 's/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(on\|yes\|1\)$/\1[\2](\3):1/I;s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(off\|no\|0\)$/\1[\2](\3):0/I' -i $tmpfile + sed 's/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):.\+$/\1{\2}(\3):1/I;s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):$/\1{\2}(\3):0/I' -i $tmpfile fields=`cat $tmpfile | \ - sed 's/^\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)/<\1>/' | \ - sed 's/^\(.\+\)$/\1{{br}}/g' | \ - tr -d '\n'| sed 's/{{br}}<\([^[]\+\[[^]]*]([^)]\+):\|[^(]\+([^)]\+):\)>/\n\1/g'` + sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):/%{>\1\2):<}%/' | \ + sed 's/^\(.\+\)$/<{br}>\1/' | \ + tr -d '\n' | \ + sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):<}%/\\n\1\2):/g'` echo "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ - sed -e 's/@/\\@/g;s/{{br}}/\\\\n/g' > $fifo + sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo echo "${fields}" | \ - sed -n -e "s/\([^[]\+\)\[\([^]]*\)\](\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ + sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ sed -e 's/@/\\@/g' > $fifo rm -f $tmpfile else -- cgit v1.2.3 From d65308d54619c41f7c72266a549ccd42ce826f11 Mon Sep 17 00:00:00 2001 From: Pawel Tomak Date: Sun, 28 Mar 2010 18:26:48 +0200 Subject: checkbox (un)check fixups. Added comments about checkboxes/radiobuttons --- examples/data/scripts/formfiller.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 5eab784..b977235 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -17,6 +17,16 @@ # Keep in mind, that if you make more than one line for fileds like input # text fields, then all lines will be inserted into as one line # +# Checkboxes/radio-buttons: to uncheck it type on of the following after the +# colon: +# no +# off +# 0 +# unchecked +# false +# or leave it blank, even without spaces +# otherwise it will be considered as checked +# # user arg 1: # edit: force editing the file (falls back to new if not found) # new: start with a new file. @@ -151,7 +161,7 @@ then option=`echo -e -n "$menu"| dmenu ${LINES} -nb "${NB}" -nf "${NF}" -sb "${SB}" -sf "${SF}" -p "${PROMPT}"` fi - sed 's/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):.\+$/\1{\2}(\3):1/I;s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):$/\1{\2}(\3):0/I' -i $keydir/$domain + sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $keydir/$domain fields=`cat $keydir/$domain | \ sed -n "/^!profile=${option}/,/^!profile=/p" | \ sed '/^!profile=/d' | \ @@ -175,7 +185,7 @@ then [ -e $tmpfile ] || exit 2 - sed 's/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):.\+$/\1{\2}(\3):1/I;s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):$/\1{\2}(\3):0/I' -i $tmpfile + sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile fields=`cat $tmpfile | \ sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):/%{>\1\2):<}%/' | \ sed 's/^\(.\+\)$/<{br}>\1/' | \ -- cgit v1.2.3 From b9b1422173211d50edb273f93fe3fcc025646dc6 Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Mon, 29 Mar 2010 08:25:17 +0200 Subject: Make it more portable (now it works also with ksh) echo behaviour differs across POSIX shells. Some shells expands strings like '\\n' in echo built-in command (ksh), some does not (bash). According to [1] both behaviours are correct. That means "echo" is not portable for strings containing backslashes. [1] advises to use printf instead. [1] http://www.opengroup.org/onlinepubs/009695399/utilities/echo.html --- examples/data/scripts/formfiller.sh | 70 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index b977235..9bac495 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -95,35 +95,35 @@ then action="new" fi -dumpFunction='function dump() { - var rv=""; - var allFrames = new Array(window); - for(f=0;f/\\\\n/g' > $fifo - echo "${fields}" | \ + printf '%s\n' "${fields}" | \ sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ sed -e 's/@/\\@/g' > $fifo elif [ "$action" = "once" ] then tmpfile=`mktemp` - echo "js "$dumpFunction" dump(); " | \ + printf 'js %s dump(); \n' "$dumpFunction" | \ socat - unix-connect:$socket | \ sed -n '/^[^(]\+([^)]\+):/p' > $tmpfile ${editor} $tmpfile @@ -191,10 +191,10 @@ then sed 's/^\(.\+\)$/<{br}>\1/' | \ tr -d '\n' | \ sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):<}%/\\n\1\2):/g'` - echo "${fields}" | \ + printf '%s\n' "${fields}" | \ sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo - echo "${fields}" | \ + printf '%s\n' "${fields}" | \ sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ sed -e 's/@/\\@/g' > $fifo rm -f $tmpfile @@ -223,7 +223,7 @@ else # login(text): # passwd(password): # - echo "js "$dumpFunction" dump(); " | \ + printf 'js %s dump(); \n' "$dumpFunction" | \ socat - unix-connect:$socket | \ sed -n '/^[^(]\+([^)]\+):/p' >> $keydir/$domain fi -- cgit v1.2.3 From 40d851f42fad2d26414df39e680c5ce52f2d71a9 Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Mon, 29 Mar 2010 12:21:56 +0200 Subject: remove comments, add modeline Treat lines begining with '>' as comments. '>' not '#', because '>' is not a valid character in XML tags/argument names. Add modeline to generated file. May be useful to autoload syntax file. --- examples/data/scripts/formfiller.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 9bac495..86bb3bf 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -27,6 +27,8 @@ # or leave it blank, even without spaces # otherwise it will be considered as checked # +# All lines starting with '>' are ignored as comments +# # user arg 1: # edit: force editing the file (falls back to new if not found) # new: start with a new file. @@ -51,6 +53,7 @@ else fi PROMPT="Choose profile" +MODELINE="> vim:ft=formfiller" keydir=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/dforms @@ -181,10 +184,13 @@ then printf 'js %s dump(); \n' "$dumpFunction" | \ socat - unix-connect:$socket | \ sed -n '/^[^(]\+([^)]\+):/p' > $tmpfile + echo "$MODELINE" >> $tmpfile ${editor} $tmpfile [ -e $tmpfile ] || exit 2 + # Remove comments + sed '/^>/d' -i $tmpfile sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile fields=`cat $tmpfile | \ sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):/%{>\1\2):<}%/' | \ -- cgit v1.2.3 From f4344aabd33e1f82093c25c22c224ca286112b41 Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Mon, 29 Mar 2010 14:37:58 +0200 Subject: Support for --- examples/data/scripts/formfiller.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 86bb3bf..0b0d7e0 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -27,8 +27,6 @@ # or leave it blank, even without spaces # otherwise it will be considered as checked # -# All lines starting with '>' are ignored as comments -# # user arg 1: # edit: force editing the file (falls back to new if not found) # new: start with a new file. @@ -110,7 +108,7 @@ dumpFunction="function dump() { \ var input; \ while(input=xp_res.iterateNext()) { \ var type=(input.type?input.type:text); \ - if(type == 'text' || type == 'password') { \ + if(type == 'text' || type == 'password' || type == 'search') { \ rv += input.name + '(' + type + '):' + input.value + '\\\\n'; \ } \ else if(type == 'checkbox' || type == 'radio') { \ @@ -135,7 +133,7 @@ insertFunction="function insert(fname, ftype, fvalue, fchecked) { \ } \ for(j=0;j\1\2):<}%/' | \ + sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \ sed 's/^\(.\+\)$/<{br}>\1/' | \ tr -d '\n' | \ - sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):<}%/\\n\1\2):/g'` + sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g'` printf '%s\n' "${fields}" | \ - sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ + sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo printf '%s\n' "${fields}" | \ sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ @@ -193,12 +191,12 @@ then sed '/^>/d' -i $tmpfile sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile fields=`cat $tmpfile | \ - sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):/%{>\1\2):<}%/' | \ + sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \ sed 's/^\(.\+\)$/<{br}>\1/' | \ tr -d '\n' | \ - sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|textarea\|password\)):<}%/\\n\1\2):/g'` + sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g'` printf '%s\n' "${fields}" | \ - sed -n -e "s/\([^(]\+\)(\(password\|text\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ + sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \ sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' > $fifo printf '%s\n' "${fields}" | \ sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \ -- cgit v1.2.3 From bc681bb8b4fb4a9974a0f3f9d282038bdde77f28 Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Mon, 29 Mar 2010 15:19:26 +0200 Subject: Add modeline in 'new', strip comments in 'load'. --- examples/data/scripts/formfiller.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 0b0d7e0..97b046b 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -162,6 +162,9 @@ then option=`echo -e -n "$menu"| dmenu ${LINES} -nb "${NB}" -nf "${NF}" -sb "${SB}" -sf "${SF}" -p "${PROMPT}"` fi + # Remove comments + sed '/^>/d' -i $tmpfile + sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $keydir/$domain fields=`cat $keydir/$domain | \ sed -n "/^!profile=${option}/,/^!profile=/p" | \ @@ -189,6 +192,7 @@ then # Remove comments sed '/^>/d' -i $tmpfile + sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile fields=`cat $tmpfile | \ sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \ @@ -205,12 +209,8 @@ then else if [ "$action" == 'new' -o "$action" == 'add' ] then - if [ "$action" == 'new' ] - then - echo "!profile=NAME_THIS_PROFILE$RANDOM" > $keydir/$domain - else - echo "!profile=NAME_THIS_PROFILE$RANDOM" >> $keydir/$domain - fi + [ "$action" == 'new' ] && echo "$MODELINE" > $keydir/$domain + echo "!profile=NAME_THIS_PROFILE$RANDOM" >> $keydir/$domain # # 2. and 3. line (tr -d and sed) are because, on gmail login for example, # tag is splited into lines -- cgit v1.2.3 From 47c12001920a79d860a67a0235ca43ba0a45dcee Mon Sep 17 00:00:00 2001 From: Paweł Zuzelski Date: Mon, 29 Mar 2010 15:27:52 +0200 Subject: kill bashizm [ == ] --- examples/data/scripts/formfiller.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 97b046b..69eca17 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -91,7 +91,7 @@ if [ "$action" != 'edit' -a "$action" != 'new' -a "$action" != 'load' -a "$acti then action="new" [ -e "$keydir/$domain" ] && action="load" -elif [ "$action" == 'edit' ] && [ ! -e "$keydir/$domain" ] +elif [ "$action" = 'edit' ] && [ ! -e "$keydir/$domain" ] then action="new" fi @@ -207,9 +207,9 @@ then sed -e 's/@/\\@/g' > $fifo rm -f $tmpfile else - if [ "$action" == 'new' -o "$action" == 'add' ] + if [ "$action" = 'new' -o "$action" = 'add' ] then - [ "$action" == 'new' ] && echo "$MODELINE" > $keydir/$domain + [ "$action" = 'new' ] && echo "$MODELINE" > $keydir/$domain echo "!profile=NAME_THIS_PROFILE$RANDOM" >> $keydir/$domain # # 2. and 3. line (tr -d and sed) are because, on gmail login for example, @@ -219,7 +219,7 @@ else # type="text" # value=""> # So, tr removes all new lines, and sed inserts new line after each > - # Next sed selects only tags and only with type == "text" or == "password" + # Next sed selects only tags and only with type = "text" or = "password" # If type is first and name is second, then another sed will change their order # so the last sed will make output # text_from_the_name_attr(text or password): -- cgit v1.2.3 From d36687c0dac5d000dfcb3054d3b7f04d6b61ff97 Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Sat, 3 Apr 2010 13:08:30 +0200 Subject: add vim syntax based folding for uzbl config --- examples/config/config | 2 ++ extras/vim/syntax/uzbl.vim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/config/config b/examples/config/config index 504c7b8..f95c7fc 100644 --- a/examples/config/config +++ b/examples/config/config @@ -387,3 +387,5 @@ set default_mode = command # Set the "home" page. set uri = uzbl.org/doesitwork/@COMMIT + +# vim: set fdm=syntax: diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index 39f2495..b2153ef 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -44,7 +44,7 @@ syn match uzblTodo /TODO:/ contained syn region uzblComment display start=/^#/ end=/$/ contains=uzblTodo " Comment headings -syn region uzblSection display start=/^# ===/ end=/$/ +syn region uzblSec matchgroup=uzblSection start=/^# ===.*$/ end=/^# ===/me=e-5 contains=ALL fold syn region uzblSubSection display start=/^# ---/ end=/$/ " Integer and float matching -- cgit v1.2.3 From f93e525d11ada652667e8b6fc162749ca4c5b9b8 Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Sat, 3 Apr 2010 13:24:49 +0200 Subject: refine vim syntax regexes --- extras/vim/syntax/uzbl.vim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index b2153ef..12c041e 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -48,9 +48,9 @@ syn region uzblSec matchgroup=uzblSection start=/^# ===.*$/ end=/^# ===/me=e-5 c syn region uzblSubSection display start=/^# ---/ end=/$/ " Integer and float matching -syn match uzblPercent display /\s\(+\|-\|\)\(\d\+.\d\+\|\d\+\)%\(\s\|\n\)/ -syn match uzblInt display /\s\(+\|-\|\)\d\+\(\s\|\n\)/ -syn match uzblFloat display /\s\(+\|-\|\)\d\+.\d\+\(\s\|\n\)/ +syn match uzblPercent display /\s[+-]\=\%(\d\+\.\)\=\d\+%\_s/ +syn match uzblInt display /\s[+-]\=\d\+\_s/ +syn match uzblFloat display /\s[+-]\=\d\+\.\d\+\_s/ " Handler arguments syn match uzblArgs display /$\d\+/ @@ -63,7 +63,7 @@ syn match uzblInternalExpand display /@[A-Z_]\+/ syn match uzblInternalExpand display /@{[A-Z_]\+}/ " Matches $ENVIRON_VAR -syn match uzblEnvironVariable display /$[A-Za-z0-9_]\+/ +syn match uzblEnvironVariable display /$\a\+\w*/ " Matches @some_var and @{some_var} syn match uzblExpand display /@[A-Za-z0-9_\.]\+/ -- cgit v1.2.3 From 67a6e66c0e2287afb5333e0683ed31c957a54d8e Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Sat, 3 Apr 2010 13:29:46 +0200 Subject: add second level folding for uzbl config --- extras/vim/syntax/uzbl.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index 12c041e..eacbb7c 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -45,7 +45,7 @@ syn region uzblComment display start=/^#/ end=/$/ contains=uzblTodo " Comment headings syn region uzblSec matchgroup=uzblSection start=/^# ===.*$/ end=/^# ===/me=e-5 contains=ALL fold -syn region uzblSubSection display start=/^# ---/ end=/$/ +syn region uzblSSec matchgroup=uzblSubSection start=/^# ---.*$/ end=/^# [=-]\{3}/me=e-5 contains=ALLBUT,uzblSec,uzblSSec fold " Integer and float matching syn match uzblPercent display /\s[+-]\=\%(\d\+\.\)\=\d\+%\_s/ -- cgit v1.2.3 From d3cbe16bf16ff63c0e3db15d645e958712bd02d8 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Sun, 4 Apr 2010 04:44:03 +0800 Subject: Huge plugin & event manager upgrades. 1. Removed unused modules 2. Re-use event handlers with identical callbacks and args. 3. Removed plugin exceptions in favour of assertions. 4. Remove useless raw_keycmd and raw_bind config vars. 5. Implemented and use `after` and `cleanup` plugin hooks (correctly) 6. EM & plugins now use the python logging module to output messages 7. Null config items are removed automatically 8. Simpler mode plugin 9. The init plugins function is called after the INSTANCE_START event 10. New optparse option to silence event echoing to stdout 11. Close instance socket on INSTANCE_EXIT before event handling 12. Caught signals are logged 13. Show times on the messages in the log file 14. Refactor bind pluin to use uzbl.bindlet directly. 15. Refactor keycmd plugin to use uzbl.keycmd directly. 16. Refactored on_event plugin to use uzbl.on_events dict over UZBLS dict 17. Refactor completion plugin to use uzbl.completion set object. 18. Modified progress plugin to use config vars instead of `@progress k = v` 19. mode_config now a defaultdict(dict) (I.e. this allows you to `uzbl.mode_config[mode][var] = value` without needing to check `mode` is in the `uzbl.mode_config` dict). 20. Removed all default mode config values. 21. Removed all `get_mode()` and `set_mode(..)` functions (and the like). 22. Setting the mode is now done via the config object directly (I.e. `uzbl.config['mode'] = 'insert'`). 23. Uses the on_set plugin to watch for 'mode' and 'default_mode' config changes. 24. Don't raise the useless NEW_ON_SET event, missing ON_SET connect. 25. Plugin and EventHandler aren't suited as dict objects. 26. Also using collections.defaultdict(list) for uzbl.handlers dict. 27. Plugin `on_set.py` allows you to attach handlers to config var changes 28. Config plugin reduced to one `uzbl.config` dict-like object. 29. Update export and connect calls in plugins. 30. The functions connect, connect_dict, export, export_dict, require, logging are exported directly to the plugin namespace. 31. Moved parse_msg into Uzbl class. 32. Generally improved comments. 33. UzblEventDaemon now an object. 34. Various variable, function & class renames. --- Makefile | 4 +- examples/config/config | 28 +- examples/data/plugins/bind.py | 63 +- examples/data/plugins/cmd_expand.py | 6 +- examples/data/plugins/completion.py | 129 ++-- examples/data/plugins/config.py | 129 ++-- examples/data/plugins/keycmd.py | 121 +--- examples/data/plugins/mode.py | 204 ++---- examples/data/plugins/on_event.py | 69 +- examples/data/plugins/on_set.py | 92 +++ examples/data/plugins/plugin_template.py | 76 -- examples/data/plugins/progress_bar.py | 152 ++-- examples/data/scripts/uzbl-event-manager | 1125 ++++++++++++++++-------------- 13 files changed, 1009 insertions(+), 1189 deletions(-) create mode 100644 examples/data/plugins/on_set.py delete mode 100644 examples/data/plugins/plugin_template.py diff --git a/Makefile b/Makefile index 5c97e4d..a995f76 100644 --- a/Makefile +++ b/Makefile @@ -63,10 +63,10 @@ test-uzbl-browser-sandbox: uzbl-browser make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-example-data cp -np ./misc/env.sh ./sandbox/env.sh source ./sandbox/env.sh && uzbl-cookie-daemon restart -nv & - source ./sandbox/env.sh && uzbl-event-manager restart -nav & + source ./sandbox/env.sh && uzbl-event-manager restart -navv & source ./sandbox/env.sh && uzbl-browser --uri http://www.uzbl.org --verbose source ./sandbox/env.sh && uzbl-cookie-daemon stop -v - source ./sandbox/env.sh && uzbl-event-manager stop -v + source ./sandbox/env.sh && uzbl-event-manager stop -ivv make DESTDIR=./sandbox uninstall rm -rf ./sandbox/usr diff --git a/examples/config/config b/examples/config/config index 504c7b8..348ad42 100644 --- a/examples/config/config +++ b/examples/config/config @@ -14,8 +14,8 @@ set mode_bind = request MODE_BIND set mode_config = request MODE_CONFIG # request ON_EVENT set on_event = request ON_EVENT -# request PROGRESS_CONFIG = -set progress = request PROGRESS_CONFIG +# request ON_SET +set on_set = request ON_SET # request MODMAP set modmap = request MODMAP # request IGNORE_KEY @@ -93,7 +93,7 @@ set hint_style = weight="bold" set mode_section = [\@[\@mode_indicator]\@] set keycmd_section = [\@[\@keycmd_prompt]\@\@modcmd\@keycmd\@completion_list] -set progress_section = \@[\@progress_format]\@ +set progress_section = \@[\@progress.output]\@ set scroll_section = \@[\@scroll_message]\@ set uri_section = \@[\@uri]\@ set name_section = \@[\@NAME]\@ @@ -104,19 +104,19 @@ set status_format = @mode_section @keycmd_sect set title_format_long = \@keycmd_prompt \@raw_modcmd \@raw_keycmd \@TITLE - Uzbl browser <\@NAME> \@SELECTED_URI -# Progress bar config -@progress width = 8 +# --- Progress bar configuration (progress_bar.py plugin) --- # %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 = +set progress.width = 8 +set progress.format = [%d>%p]%c +set progress.done = = +set progress.pending = + +# Or using a spinner: +#set progress.format = [%d%s%p] +#set progress.spinner = -\\|/ +#set progress.done = - +#set progress.pending = # === Core settings ========================================================== diff --git a/examples/data/plugins/bind.py b/examples/data/plugins/bind.py index a1a5d89..5b13476 100644 --- a/examples/data/plugins/bind.py +++ b/examples/data/plugins/bind.py @@ -11,10 +11,6 @@ And it is also possible to execute a function on activation: import sys import re -import pprint - -# Hold the bind dicts for each uzbl instance. -UZBLS = {} # Commonly used regular expressions. MOD_START = re.compile('^<([A-Z][A-Za-z0-9-_]*)>').match @@ -62,9 +58,9 @@ class Bindlet(object): if self.last_mode: mode, self.last_mode = self.last_mode, None - self.uzbl.set_mode(mode) + self.uzbl.config['mode'] = mode - self.uzbl.set('keycmd_prompt') + del self.uzbl.config['keycmd_prompt'] def stack(self, bind, args, depth): @@ -76,10 +72,10 @@ class Bindlet(object): return - current_mode = self.uzbl.get_mode() - if current_mode != 'stack': - self.last_mode = current_mode - self.uzbl.set_mode('stack') + mode = self.uzbl.config.get('mode', None) + if mode != 'stack': + self.last_mode = mode + self.uzbl.config['mode'] = 'stack' self.stack_binds = [bind,] self.args += args @@ -97,7 +93,7 @@ class Bindlet(object): self.uzbl.clear_keycmd() if prompt: - self.uzbl.set('keycmd_prompt', prompt) + self.uzbl.config['keycmd_prompt'] = prompt if set and is_cmd: self.uzbl.send(set) @@ -111,7 +107,7 @@ class Bindlet(object): the filtered stack list and modkey & non-stack globals.''' if mode is None: - mode = self.uzbl.get_mode() + mode = self.uzbl.config.get('mode', None) if not mode: mode = 'global' @@ -145,24 +141,6 @@ class Bindlet(object): self.globals.append(bind) -def add_instance(uzbl, *args): - UZBLS[uzbl] = Bindlet(uzbl) - - -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_bindlet(uzbl): - '''Return the bind tracklet for the given uzbl instance.''' - - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - def ismodbind(glob): '''Return True if the glob specifies a modbind.''' @@ -324,7 +302,7 @@ def exec_bind(uzbl, bind, *args, **kargs): def mode_bind(uzbl, modes, glob, handler=None, *args, **kargs): '''Add a mode bind.''' - bindlet = get_bindlet(uzbl) + bindlet = uzbl.bindlet if not hasattr(modes, '__iter__'): modes = unicode(modes).split(',') @@ -400,7 +378,8 @@ def mode_changed(uzbl, mode): '''Clear the stack on all non-stack mode changes.''' if mode != 'stack': - get_bindlet(uzbl).reset() + uzbl.bindlet.reset() + uzbl.clear_keycmd() def match_and_exec(uzbl, bind, depth, keylet, bindlet): @@ -440,7 +419,7 @@ def match_and_exec(uzbl, bind, depth, keylet, bindlet): args = bindlet.args + args exec_bind(uzbl, bind, *args) if not has_args or on_exec: - uzbl.set_mode() + del uzbl.config['mode'] bindlet.reset() uzbl.clear_current() @@ -448,7 +427,7 @@ def match_and_exec(uzbl, bind, depth, keylet, bindlet): def key_event(uzbl, keylet, mod_cmd=False, on_exec=False): - bindlet = get_bindlet(uzbl) + bindlet = uzbl.bindlet depth = bindlet.depth for bind in bindlet.get_binds(): t = bind[depth] @@ -463,12 +442,14 @@ def key_event(uzbl, keylet, mod_cmd=False, on_exec=False): # Return to the previous mode if the KEYCMD_EXEC keycmd doesn't match any # binds in the stack mode. if on_exec and not mod_cmd and depth and depth == bindlet.depth: - uzbl.set_mode() + del uzbl.config['mode'] +# plugin init hook def init(uzbl): - # Event handling hooks. - uzbl.connect_dict({ + '''Export functions and connect handlers to events.''' + + connect_dict(uzbl, { 'BIND': parse_bind, 'MODE_BIND': parse_mode_bind, 'MODE_CHANGED': mode_changed, @@ -481,12 +462,10 @@ def init(uzbl): for mod_cmd in range(2): for on_exec in range(2): event = events[mod_cmd][on_exec] - uzbl.connect(event, key_event, bool(mod_cmd), bool(on_exec)) + connect(uzbl, event, key_event, bool(mod_cmd), bool(on_exec)) - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export_dict({ + export_dict(uzbl, { 'bind': bind, 'mode_bind': mode_bind, - 'get_bindlet': get_bindlet, + 'bindlet': Bindlet(uzbl), }) diff --git a/examples/data/plugins/cmd_expand.py b/examples/data/plugins/cmd_expand.py index 3f6ae2b..b007975 100644 --- a/examples/data/plugins/cmd_expand.py +++ b/examples/data/plugins/cmd_expand.py @@ -35,8 +35,6 @@ def cmd_expand(uzbl, cmd, args): return cmd - +# plugin init hook def init(uzbl): - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export('cmd_expand', cmd_expand) + export(uzbl, 'cmd_expand', cmd_expand) diff --git a/examples/data/plugins/completion.py b/examples/data/plugins/completion.py index 8cea203..e8c7f34 100644 --- a/examples/data/plugins/completion.py +++ b/examples/data/plugins/completion.py @@ -1,19 +1,10 @@ '''Keycmd completion.''' -# A list of functions this plugin exports to be used via uzbl object. -__export__ = ['start_completion', 'get_completion_dict'] - import re -# Holds the per-instance completion dicts. -UZBLS = {} - # Completion level NONE, ONCE, LIST, COMPLETE = range(4) -# Default instance dict. -DEFAULTS = {'completions': [], 'level': NONE, 'lock': False} - # The reverse keyword finding re. FIND_SEGMENT = re.compile("(\@[\w_]+|set[\s]+[\w_]+|[\w_]+)$").findall @@ -21,39 +12,17 @@ FIND_SEGMENT = re.compile("(\@[\w_]+|set[\s]+[\w_]+|[\w_]+)$").findall LIST_FORMAT = " %s " ITEM_FORMAT = "%s%s" - def escape(str): return str.replace("@", "\@") -def add_instance(uzbl, *args): - UZBLS[uzbl] = dict(DEFAULTS) - - # Make sure the config keys for all possible completions are known. - uzbl.send('dump_config_as_events') - - -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del UZBLS[uzbl] - - -def get_completion_dict(uzbl): - '''Get data stored for an instance.''' - - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - def get_incomplete_keyword(uzbl): '''Gets the segment of the keycmd leading up to the cursor position and uses a regular expression to search backwards finding parially completed keywords or @variables. Returns a null string if the correct completion conditions aren't met.''' - keylet = uzbl.get_keylet() + keylet = uzbl.keylet left_segment = keylet.keycmd[:keylet.cursor] partial = (FIND_SEGMENT(left_segment) + ['',])[0].lstrip() if partial.startswith('set '): @@ -65,9 +34,8 @@ def get_incomplete_keyword(uzbl): def stop_completion(uzbl, *args): '''Stop command completion and return the level to NONE.''' - d = get_completion_dict(uzbl) - d['level'] = NONE - uzbl.set('completion_list') + uzbl.completion.level = NONE + del uzbl.config['completion_list'] def complete_completion(uzbl, partial, hint, set_completion=False): @@ -99,46 +67,46 @@ def update_completion_list(uzbl, *args): if not partial: return stop_completion(uzbl) - d = get_completion_dict(uzbl) - if d['level'] < LIST: + if uzbl.completion.level < LIST: return - hints = [h for h in d['completions'] if h.startswith(partial)] + hints = filter(lambda h: h.startswith(partial), uzbl.completion) if not hints: - return uzbl.set('completion_list') + del uzbl.config['completion_list'] + return j = len(partial) l = [ITEM_FORMAT % (escape(h[:j]), h[j:]) for h in sorted(hints)] - uzbl.set('completion_list', LIST_FORMAT % ' '.join(l)) + uzbl.config['completion_list'] = LIST_FORMAT % ' '.join(l) def start_completion(uzbl, *args): - d = get_completion_dict(uzbl) - if d['lock']: + comp = uzbl.completion + if comp.locked: return (partial, set_completion) = get_incomplete_keyword(uzbl) if not partial: return stop_completion(uzbl) - if d['level'] < COMPLETE: - d['level'] += 1 + if comp.level < COMPLETE: + comp.level += 1 - hints = [h for h in d['completions'] if h.startswith(partial)] + hints = filter(lambda h: h.startswith(partial), comp) if not hints: return elif len(hints) == 1: - d['lock'] = True + comp.lock() complete_completion(uzbl, partial, hints[0], set_completion) - d['lock'] = False + comp.unlock() return - elif partial in hints and d['level'] == COMPLETE: - d['lock'] = True + elif partial in hints and comp.level == COMPLETE: + comp.lock() complete_completion(uzbl, partial, partial, set_completion) - d['lock'] = False + comp.unlock() return smalllen, smallest = sorted([(len(h), h) for h in hints])[0] @@ -156,51 +124,56 @@ def start_completion(uzbl, *args): common += char if common: - d['lock'] = True + comp.lock() partial_completion(uzbl, partial, partial+common) - d['lock'] = False + comp.unlock() update_completion_list(uzbl) -def add_builtins(uzbl, args): +def add_builtins(uzbl, builtins): '''Pump the space delimited list of builtin commands into the builtin list.''' - completions = get_completion_dict(uzbl)['completions'] - builtins = filter(None, map(unicode.strip, args.split(" "))) - for builtin in builtins: - if builtin not in completions: - completions.append(builtin) + uzbl.completion.update(builtins.split()) def add_config_key(uzbl, key, value): '''Listen on the CONFIG_CHANGED event and add config keys to the variable list for @var like expansion support.''' - completions = get_completion_dict(uzbl)['completions'] - key = "@%s" % key - if key not in completions: - completions.append(key) + uzbl.completion.add("@%s" % key) + + +class Completions(set): + def __init__(self): + set.__init__(self) + self.locked = False + self.level = NONE + + def lock(self): + self.locked = True + + def unlock(self): + self.locked = False def init(uzbl): - # Event handling hooks. - uzbl.connect_dict({ - 'BUILTINS': add_builtins, - 'CONFIG_CHANGED': add_config_key, - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, - 'KEYCMD_CLEARED': stop_completion, - 'KEYCMD_EXEC': stop_completion, - 'KEYCMD_UPDATE': update_completion_list, - 'START_COMPLETION': start_completion, - 'STOP_COMPLETION': stop_completion, + '''Export functions and connect handlers to events.''' + + export_dict(uzbl, { + 'completion': Completions(), + 'start_completion': start_completion, }) - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export_dict({ - 'get_completion_dict': get_completion_dict, - 'start_completion': start_completion, + connect_dict(uzbl, { + 'BUILTINS': add_builtins, + 'CONFIG_CHANGED': add_config_key, + 'KEYCMD_CLEARED': stop_completion, + 'KEYCMD_EXEC': stop_completion, + 'KEYCMD_UPDATE': update_completion_list, + 'START_COMPLETION': start_completion, + 'STOP_COMPLETION': stop_completion, }) + + uzbl.send('dump_config_as_events') diff --git a/examples/data/plugins/config.py b/examples/data/plugins/config.py index 4a848a3..ed2d761 100644 --- a/examples/data/plugins/config.py +++ b/examples/data/plugins/config.py @@ -1,97 +1,90 @@ -import re -import types +from re import compile +from types import BooleanType +from UserDict import DictMixin -__export__ = ['set', 'get_config'] - -VALIDKEY = 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) +valid_key = compile('^[A-Za-z0-9_\.]+$').match +types = {'int': int, 'float': float, 'str': unicode} +escape = lambda s: unicode(s).replace('\n', '\\n') +class Config(DictMixin): + def __init__(self, uzbl): + self.uzbl = uzbl -def set(uzbl, key, value='', config=None, force=False): - '''Sends a: "set key = value" command to the uzbl instance. If force is - False then only send a set command if the values aren't equal.''' + # Create the base dict and map allowed methods to `self`. + self.data = data = {} - if type(value) == types.BooleanType: - value = int(value) + methods = ['__contains__', '__getitem__', '__iter__', + '__len__', 'get', 'has_key', 'items', 'iteritems', + 'iterkeys', 'itervalues', 'values'] - else: - value = unicode(value) + for method in methods: + setattr(self, method, getattr(data, method)) - if not VALIDKEY(key): - raise KeyError("%r" % key) - value = escape(value) - if '\n' in value: - value = value.replace("\n", "\\n") + def __setitem__(self, key, value): + self.set(key, value) - if not force: - if config is None: - config = get_config(uzbl) + def __delitem__(self, key): + self.set(key) - if key in config and config[key] == value: - return + def update(self, other=None, **kwargs): + if other is None: + other = {} - uzbl.send('set %s = %s' % (key, value)) + for (key, value) in dict(other).items() + kwargs.items(): + self[key] = value -class ConfigDict(dict): - def __init__(self, uzbl): - self._uzbl = uzbl + def set(self, key, value='', force=False): + '''Generates a `set = ` command string to send to the + current uzbl instance. - def __setitem__(self, key, value): - '''Makes "config[key] = value" a wrapper for the set function.''' + Note that the config dict isn't updated by this function. The config + dict is only updated after a successful `VARIABLE_SET ..` event + returns from the uzbl instance.''' - set(self._uzbl, key, value, config=self) + assert valid_key(key) + if type(value) == BooleanType: + value = int(value) -def add_instance(uzbl, *args): - UZBLS[uzbl] = ConfigDict(uzbl) + else: + value = escape(value) + if not force and key in self and self[key] == value: + return -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del uzbl + self.uzbl.send(u'set %s = %s' % (key, value)) -def get_config(uzbl): - if uzbl not in UZBLS: - add_instance(uzbl) +def parse_set_event(uzbl, args): + '''Parse `VARIABLE_SET ` event and load the + (key, value) pair into the `uzbl.config` dict.''' - return UZBLS[uzbl] + (key, type, raw_value) = (args.split(' ', 2) + ['',])[:3] + assert valid_key(key) + assert type in types -def variable_set(uzbl, args): - config = get_config(uzbl) + new_value = types[type](raw_value) + old_value = uzbl.config.get(key, None) - key, type, value = list(args.split(' ', 2) + ['',])[:3] - old = config[key] if key in config else None - value = TYPECONVERT[type](value) + # Update new value. + uzbl.config.data[key] = new_value - dict.__setitem__(config, key, value) + if old_value != new_value: + uzbl.event('CONFIG_CHANGED', key, new_value) - if old != value: - uzbl.event("CONFIG_CHANGED", key, value) + # Cleanup null config values. + if type == 'str' and not new_value: + del uzbl.config.data[key] +# plugin init hook def init(uzbl): - # Event handling hooks. - uzbl.connect_dict({ - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, - 'VARIABLE_SET': variable_set, - }) - - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export_dict({ - 'get_config': get_config, - 'set': set, - }) + export(uzbl, 'config', Config(uzbl)) + connect(uzbl, 'VARIABLE_SET', parse_set_event) + +# plugin cleanup hook +def cleanup(uzbl): + uzbl.config.data.clear() diff --git a/examples/data/plugins/keycmd.py b/examples/data/plugins/keycmd.py index c119077..b600afe 100644 --- a/examples/data/plugins/keycmd.py +++ b/examples/data/plugins/keycmd.py @@ -1,8 +1,5 @@ import re -# Hold the keylets. -UZBLS = {} - # Keycmd format which includes the markup for the cursor. KEYCMD_FORMAT = "%s%s%s" MODCMD_FORMAT = " %s " @@ -38,9 +35,6 @@ class Keylet(object): self.ignores = {} self.additions = {} - # Keylet string repr cache. - self._repr_cache = None - def get_keycmd(self): '''Get the keycmd-part of the keylet.''' @@ -106,9 +100,6 @@ class Keylet(object): def __repr__(self): '''Return a string representation of the keylet.''' - if self._repr_cache: - return self._repr_cache - l = [] if self.is_modcmd: l.append('modcmd=%r' % self.get_modcmd()) @@ -119,8 +110,7 @@ class Keylet(object): if self.keycmd: l.append('keycmd=%r' % self.get_keycmd()) - self._repr_cache = '' % ', '.join(l) - return self._repr_cache + return '' % ', '.join(l) def add_modmap(uzbl, key, map): @@ -140,7 +130,7 @@ def add_modmap(uzbl, key, map): ''' assert len(key) - modmaps = get_keylet(uzbl).modmaps + modmaps = uzbl.keylet.modmaps if key[0] == "<" and key[-1] == ">": key = key[1:-1] @@ -171,7 +161,7 @@ def add_key_ignore(uzbl, glob): ''' assert len(glob) > 1 - ignores = get_keylet(uzbl).ignores + ignores = uzbl.keylet.ignores glob = "<%s>" % glob.strip("<> ") restr = glob.replace('*', '[^\s]*') @@ -197,7 +187,7 @@ def add_modkey_addition(uzbl, modkeys, result): ... ''' - additions = get_keylet(uzbl).additions + additions = uzbl.keylet.additions modkeys = set(modkeys) assert len(modkeys) and result and result not in modkeys @@ -220,65 +210,34 @@ def modkey_addition_parse(uzbl, modkeys): add_modkey_addition(uzbl, keys[:-1], keys[-1]) -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._repr_cache = False - return keylet - - def clear_keycmd(uzbl): '''Clear the keycmd for this uzbl instance.''' - k = get_keylet(uzbl) + k = uzbl.keylet k.keycmd = '' k.cursor = 0 - k._repr_cache = False - uzbl.set('keycmd') - uzbl.set('raw_keycmd') + del uzbl.config['keycmd'] uzbl.event('KEYCMD_CLEARED') def clear_modcmd(uzbl, clear_held=False): '''Clear the modcmd for this uzbl instance.''' - k = get_keylet(uzbl) + k = uzbl.keylet k.modcmd = '' k.is_modcmd = False - k._repr_cache = False if clear_held: k.ignored = set() k.held = set() - uzbl.set('modcmd') - uzbl.set('raw_modcmd') + del uzbl.config['modcmd'] uzbl.event('MODCMD_CLEARED') def clear_current(uzbl): '''Clear the modcmd if is_modcmd else clear keycmd.''' - k = get_keylet(uzbl) - if k.is_modcmd: + if uzbl.keylet.is_modcmd: clear_modcmd(uzbl) else: @@ -296,7 +255,6 @@ def focus_changed(uzbl, *args): def update_event(uzbl, k, execute=True): '''Raise keycmd & modcmd update events.''' - config = uzbl.get_config() keycmd, modcmd = k.get_keycmd(), k.get_modcmd() if k.is_modcmd: @@ -305,32 +263,28 @@ def update_event(uzbl, k, execute=True): else: uzbl.event('KEYCMD_UPDATE', k) - if 'modcmd_updates' not in config or config['modcmd_updates'] == '1': + if uzbl.config.get('modcmd_updates', '1') == '1': new_modcmd = k.get_modcmd() if not new_modcmd: - uzbl.set('modcmd', config=config) - uzbl.set('raw_modcmd', config=config) + del uzbl.config['modcmd'] elif new_modcmd == modcmd: - uzbl.set('raw_modcmd', escape(modcmd), config=config) - uzbl.set('modcmd', MODCMD_FORMAT % uzbl_escape(modcmd), - config=config) + uzbl.config['modcmd'] = MODCMD_FORMAT % uzbl_escape(modcmd) - if 'keycmd_events' in config and config['keycmd_events'] != '1': + if uzbl.config.get('keycmd_events', '1') != '1': return new_keycmd = k.get_keycmd() if not new_keycmd: - uzbl.set('keycmd', config=config) - uzbl.set('raw_keycmd', config=config) + del uzbl.config['keycmd'] elif new_keycmd == keycmd: # Generate the pango markup for the cursor in the keycmd. curchar = keycmd[k.cursor] if k.cursor < len(keycmd) else ' ' chunks = [keycmd[:k.cursor], curchar, keycmd[k.cursor+1:]] value = KEYCMD_FORMAT % tuple(map(uzbl_escape, chunks)) - uzbl.set('keycmd', value, config=config) - uzbl.set('raw_keycmd', escape(keycmd), config=config) + + uzbl.config['keycmd'] = value def inject_str(str, index, inj): @@ -344,7 +298,7 @@ def get_keylet_and_key(uzbl, key, add=True): by the modmapping or modkey addition rules. Return None if the key is ignored.''' - keylet = get_keylet(uzbl) + keylet = uzbl.keylet key = keylet.modmap_key(key) if len(key) == 1: return (keylet, key) @@ -385,12 +339,11 @@ def key_press(uzbl, key): k.cursor += 1 elif not k.held and len(key) == 1: - config = uzbl.get_config() - if 'keycmd_events' in config and config['keycmd_events'] != '1': + + if uzbl.config.get('keycmd_events', '1') != '1': k.keycmd = '' k.cursor = 0 - uzbl.set('keycmd', config=config) - uzbl.set('raw_keycmd', config=config) + del uzbl.config['keycmd'] return k.keycmd = inject_str(k.keycmd, k.cursor, key) @@ -429,9 +382,8 @@ def key_release(uzbl, key): def set_keycmd(uzbl, keycmd): '''Allow setting of the keycmd externally.''' - k = get_keylet(uzbl) + k = uzbl.keylet k.keycmd = keycmd - k._repr_cache = None k.cursor = len(keycmd) update_event(uzbl, k, False) @@ -439,9 +391,8 @@ def set_keycmd(uzbl, keycmd): def inject_keycmd(uzbl, keycmd): '''Allow injecting of a string into the keycmd at the cursor position.''' - k = get_keylet(uzbl) + k = uzbl.keylet k.keycmd = inject_str(k.keycmd, k.cursor, keycmd) - k._repr_cache = None k.cursor += len(keycmd) update_event(uzbl, k, False) @@ -449,9 +400,8 @@ def inject_keycmd(uzbl, keycmd): def append_keycmd(uzbl, keycmd): '''Allow appening of a string to the keycmd.''' - k = get_keylet(uzbl) + k = uzbl.keylet k.keycmd += keycmd - k._repr_cache = None k.cursor = len(k.keycmd) update_event(uzbl, k, False) @@ -460,7 +410,7 @@ def keycmd_strip_word(uzbl, sep): ''' Removes the last word from the keycmd, similar to readline ^W ''' sep = sep or ' ' - k = get_keylet(uzbl) + k = uzbl.keylet if not k.keycmd: return @@ -475,7 +425,7 @@ def keycmd_strip_word(uzbl, sep): def keycmd_backspace(uzbl, *args): '''Removes the character at the cursor position in the keycmd.''' - k = get_keylet(uzbl) + k = uzbl.keylet if not k.keycmd: return @@ -487,7 +437,7 @@ def keycmd_backspace(uzbl, *args): def keycmd_delete(uzbl, *args): '''Removes the character after the cursor position in the keycmd.''' - k = get_keylet(uzbl) + k = uzbl.keylet if not k.keycmd: return @@ -499,8 +449,7 @@ def keycmd_exec_current(uzbl, *args): '''Raise a KEYCMD_EXEC with the current keylet and then clear the keycmd.''' - k = get_keylet(uzbl) - uzbl.event('KEYCMD_EXEC', k) + uzbl.event('KEYCMD_EXEC', uzbl.keylet) clear_keycmd(uzbl) @@ -508,7 +457,7 @@ def set_cursor_pos(uzbl, index): '''Allow setting of the cursor position externally. Supports negative indexing and relative stepping with '+' and '-'.''' - k = get_keylet(uzbl) + k = uzbl.keylet if index == '-': cursor = k.cursor - 1 @@ -530,18 +479,16 @@ def set_cursor_pos(uzbl, index): update_event(uzbl, k, False) +# plugin init hook def init(uzbl): - '''Connect handlers to uzbl events.''' + '''Export functions and connect handlers to events.''' - # Event handling hooks. - uzbl.connect_dict({ + connect_dict(uzbl, { 'APPEND_KEYCMD': append_keycmd, 'FOCUS_GAINED': focus_changed, 'FOCUS_LOST': focus_changed, 'IGNORE_KEY': add_key_ignore, 'INJECT_KEYCMD': inject_keycmd, - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, 'KEYCMD_BACKSPACE': keycmd_backspace, 'KEYCMD_DELETE': keycmd_delete, 'KEYCMD_EXEC_CURRENT': keycmd_exec_current, @@ -554,9 +501,7 @@ def init(uzbl): 'SET_KEYCMD': set_keycmd, }) - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export_dict({ + export_dict(uzbl, { 'add_key_ignore': add_key_ignore, 'add_modkey_addition': add_modkey_addition, 'add_modmap': add_modmap, @@ -564,8 +509,8 @@ def init(uzbl): 'clear_current': clear_current, 'clear_keycmd': clear_keycmd, 'clear_modcmd': clear_modcmd, - 'get_keylet': get_keylet, 'inject_keycmd': inject_keycmd, + 'keylet': Keylet(), 'set_cursor_pos': set_cursor_pos, 'set_keycmd': set_keycmd, }) diff --git a/examples/data/plugins/mode.py b/examples/data/plugins/mode.py index 54d865a..e0de706 100644 --- a/examples/data/plugins/mode.py +++ b/examples/data/plugins/mode.py @@ -1,176 +1,68 @@ -import sys -import re +from collections import defaultdict -__export__ = ['set_mode', 'get_mode', 'set_mode_config', 'get_mode_config'] +def parse_mode_config(uzbl, args): + '''Parse `MODE_CONFIG = ` event and update config if + the `` is the current mode.''' -UZBLS = {} + ustrip = unicode.strip + args = unicode(args) -DEFAULTS = { - 'mode': '', - 'modes': { - 'insert': { - 'forward_keys': True, - 'keycmd_events': False, - 'modcmd_updates': False, - 'mode_indicator': 'I'}, - 'command': { - 'forward_keys': False, - 'keycmd_events': True, - 'modcmd_updates': True, - 'mode_indicator': 'C'}}} + assert args.strip(), "missing mode config args" + (mode, args) = map(ustrip, (args.strip().split(' ', 1) + ['',])[:2]) -FINDSPACES = re.compile("\s+") -VALID_KEY = re.compile("^[\w_]+$").match + assert args.strip(), "missing mode config set arg" + (key, value) = map(ustrip, (args.strip().split('=', 1) + [None,])[:2]) + assert key and value is not None, "invalid mode config set syntax" + uzbl.mode_config[mode][key] = value + if uzbl.config.get('mode', None) == mode: + uzbl.config[key] = value -def add_instance(uzbl, *args): - UZBLS[uzbl] = dict(DEFAULTS) +def default_mode_updated(uzbl, var, mode): + if mode and not uzbl.config.get('mode', None): + logger.debug('setting mode to default %r' % mode) + uzbl.config['mode'] = mode -def del_instance(uzbl, *args): - if uzbl in UZBLS: - del UZBLS[uzbl] - -def get_mode_dict(uzbl): - '''Return the mode dict for an instance.''' - - if uzbl not in UZBLS: - add_instance(uzbl) - - return UZBLS[uzbl] - - -def get_mode_config(uzbl, mode): - '''Return the mode config for a given 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 mode_changed(uzbl, mode): - '''The mode has just been changed, now set the per-mode config.''' - - if get_mode(uzbl) != mode: +def mode_updated(uzbl, var, mode): + if not mode: + mode = uzbl.config.get('default_mode', 'command') + logger.debug('setting mode to default %r' % mode) + uzbl.config['mode'] = mode return - config = uzbl.get_config() - mode_config = get_mode_config(uzbl, mode) - for (key, value) in mode_config.items(): - uzbl.set(key, value, config=config) - - if 'mode_indicator' not in mode_config: - config['mode_indicator'] = mode - - uzbl.clear_keycmd() - uzbl.clear_modcmd() - - -def set_mode(uzbl, mode=None): - '''Set the mode and raise the MODE_CHANGED event if the mode has changed. - Fallback on the default mode if no mode argument was given and the default - mode is not null.''' - - config = uzbl.get_config() - mode_dict = get_mode_dict(uzbl) - if mode is None: - mode_dict['mode'] = '' - if 'default_mode' in config: - mode = config['default_mode'] - - else: - mode = 'command' - - if not VALID_KEY(mode): - raise KeyError("invalid mode name: %r" % mode) - - if 'mode' not in config or config['mode'] != mode: - config['mode'] = mode + # Load mode config + mode_config = uzbl.mode_config.get(mode, None) + if mode_config: + uzbl.config.update(mode_config) - elif mode_dict['mode'] != mode: - mode_dict['mode'] = mode - uzbl.event("MODE_CHANGED", mode) + uzbl.send('event MODE_CONFIRM %s' % mode) -def config_changed(uzbl, key, value): - '''Check for mode related config changes.''' +def confirm_change(uzbl, mode): + if mode and uzbl.config.get('mode', None) == mode: + uzbl.event('MODE_CHANGED', mode) - value = None if not value else value - if key == 'default_mode': - if not get_mode(uzbl): - set_mode(uzbl, value) - elif key == 'mode': - set_mode(uzbl, value) - - -def set_mode_config(uzbl, mode, key, value): - '''Set mode specific configs. If the mode being modified is the current - mode then apply the changes on the go.''' - - assert VALID_KEY(mode) and VALID_KEY(key) - - mode_config = get_mode_config(uzbl, mode) - mode_config[key] = value - - if get_mode(uzbl) == mode: - uzbl.set(key, value) - - -def mode_config(uzbl, args): - '''Parse mode config events.''' - - split = map(unicode.strip, FINDSPACES.split(args.lstrip(), 1)) - if len(split) != 2: - raise SyntaxError('invalid mode config syntax: %r' % args) - - mode, set = split - split = map(unicode.strip, set.split('=', 1)) - if len(split) != 2: - raise SyntaxError('invalid set syntax: %r' % args) - - key, value = split - set_mode_config(uzbl, mode, key, value) - - -def toggle_modes(uzbl, modes): - '''Toggle or cycle between or through a list of modes.''' - - assert len(modes.strip()) - - modelist = filter(None, map(unicode.strip, modes.split(' '))) - mode = get_mode(uzbl) - - index = 0 - if mode in modelist: - index = (modelist.index(mode)+1) % len(modelist) - - set_mode(uzbl, modelist[index]) +# plugin init hook +def init(uzbl): + require('config') + require('on_set') + # Usage `uzbl.mode_config[mode][key] = value` + export(uzbl, 'mode_config', defaultdict(dict)) -def init(uzbl): - # Event handling hooks. - uzbl.connect_dict({ - 'CONFIG_CHANGED': config_changed, - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, - 'MODE_CHANGED': mode_changed, - 'MODE_CONFIG': mode_config, - 'TOGGLE_MODES': toggle_modes, + connect_dict(uzbl, { + 'MODE_CONFIG': parse_mode_config, + 'MODE_CONFIRM': confirm_change, }) - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export_dict({ - 'get_mode': get_mode, - 'get_mode_config': get_mode_config, - 'set_mode': set_mode, - 'set_mode_config': set_mode_config, - }) +# plugin after hook +def after(uzbl): + uzbl.on_set('mode', mode_updated) + uzbl.on_set('default_mode', default_mode_updated) + +# plugin cleanup hook +def cleanup(uzbl): + uzbl.mode_config.clear() diff --git a/examples/data/plugins/on_event.py b/examples/data/plugins/on_event.py index b9c504a..5142275 100644 --- a/examples/data/plugins/on_event.py +++ b/examples/data/plugins/on_event.py @@ -20,36 +20,11 @@ Usage: import sys import re -__export__ = ['get_on_events', 'on_event'] - -UZBLS = {} - - -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 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) + events = uzbl.on_events event = kargs['on_event'] if event not in events: return @@ -65,9 +40,9 @@ def on_event(uzbl, event, cmd): '''Add a new event to watch and respond to.''' event = event.upper() - events = get_on_events(uzbl) + events = uzbl.on_events if event not in events: - uzbl.connect(event, event_handler, on_event=event) + connect(uzbl, event, event_handler, on_event=event) events[event] = [] cmds = events[event] @@ -80,28 +55,28 @@ def parse_on_event(uzbl, args): Syntax: "event ON_EVENT 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) + args = args.strip() + assert args, 'missing on event arguments' - event, cmd = split - on_event(uzbl, event, cmd) + (event, command) = (args.split(' ', 1) + ['',])[:2] + assert event and command, 'missing on event command' + on_event(uzbl, event, command) +# plugin init hook def init(uzbl): - # Event handling hooks. - uzbl.connect_dict({ - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, - 'ON_EVENT': parse_on_event, - }) + '''Export functions and connect handlers to events.''' + + connect(uzbl, 'ON_EVENT', parse_on_event) - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.export_dict({ - 'get_on_events': get_on_events, - 'on_event': on_event, + export_dict(uzbl, { + 'on_event': on_event, + 'on_events': {}, }) + +# plugin cleanup hook +def cleanup(uzbl): + for handlers in uzbl.on_events.values(): + del handlers[:] + + uzbl.on_events.clear() diff --git a/examples/data/plugins/on_set.py b/examples/data/plugins/on_set.py new file mode 100644 index 0000000..130b816 --- /dev/null +++ b/examples/data/plugins/on_set.py @@ -0,0 +1,92 @@ +from re import compile +from functools import partial + +valid_glob = compile('^[A-Za-z0-9_\*\.]+$').match + +def make_matcher(glob): + '''Make matcher function from simple glob.''' + + pattern = "^%s$" % glob.replace('*', '[^\s]*') + return compile(pattern).match + + +def exec_handlers(uzbl, handlers, key, arg): + '''Execute the on_set handlers that matched the key.''' + + for handler in handlers: + if callable(handler): + handler(key, arg) + + else: + uzbl.send(uzbl.cmd_expand(handler, [key, arg])) + + +def check_for_handlers(uzbl, key, arg): + '''Check for handlers for the current key.''' + + for (matcher, handlers) in uzbl.on_sets.values(): + if matcher(key): + exec_handlers(uzbl, handlers, key, arg) + + +def on_set(uzbl, glob, handler, prepend=True): + '''Add a new handler for a config key change. + + Structure of the `uzbl.on_sets` dict: + { glob : ( glob matcher function, handlers list ), .. } + ''' + + assert valid_glob(glob) + + while '**' in glob: + glob = glob.replace('**', '*') + + if callable(handler): + orig_handler = handler + if prepend: + handler = partial(handler, uzbl) + + else: + orig_handler = handler = unicode(handler) + + if glob in uzbl.on_sets: + (matcher, handlers) = uzbl.on_sets[glob] + handlers.append(handler) + + else: + matcher = make_matcher(glob) + uzbl.on_sets[glob] = (matcher, [handler,]) + + uzbl.logger.info('on set %r call %r' % (glob, orig_handler)) + + +def parse_on_set(uzbl, args): + '''Parse `ON_SET ` event then pass arguments to the + `on_set(..)` function.''' + + (glob, command) = (args.split(' ', 1) + [None,])[:2] + assert glob and command and valid_glob(glob) + on_set(uzbl, glob, command) + + +# plugins init hook +def init(uzbl): + require('config') + require('cmd_expand') + + export_dict(uzbl, { + 'on_sets': {}, + 'on_set': on_set, + }) + + connect_dict(uzbl, { + 'ON_SET': parse_on_set, + 'CONFIG_CHANGED': check_for_handlers, + }) + +# plugins cleanup hook +def cleanup(uzbl): + for (matcher, handlers) in uzbl.on_sets.values(): + del handlers[:] + + uzbl.on_sets.clear() diff --git a/examples/data/plugins/plugin_template.py b/examples/data/plugins/plugin_template.py deleted file mode 100644 index 565a999..0000000 --- a/examples/data/plugins/plugin_template.py +++ /dev/null @@ -1,76 +0,0 @@ -'''Plugin template.''' - -# 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: - uzbl.connect_dict({ - # event name function - 'INSTANCE_START': add_instance, - 'INSTANCE_EXIT': del_instance, - 'MYPLUGIN_EVENT': myplugin_event_parser, - }) - - # Or connect a handler to an event manually and supply additional optional - # arguments: - #uzbl.connect("MYOTHER_EVENT", myother_event_parser, True, limit=20) - - # Function exports to the uzbl object, `function(uzbl, *args, ..)` - # becomes `uzbl.function(*args, ..)`. - uzbl.connect_dict({ - # external name function - 'myplugin_function': myplugin_function, - }) diff --git a/examples/data/plugins/progress_bar.py b/examples/data/plugins/progress_bar.py index 89ba175..b2edffc 100644 --- a/examples/data/plugins/progress_bar.py +++ b/examples/data/plugins/progress_bar.py @@ -1,39 +1,7 @@ -import sys +UPDATES = 0 -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. +def update_progress(uzbl, progress=None): + '''Updates the progress.output variable on LOAD_PROGRESS update. The current substitution options are: %d = done char * done @@ -44,116 +12,82 @@ def update_progress(uzbl, prog=None): %t = percent pending %o = int pending %r = sprites + + Default configuration options: + progress.format = [%d>%p]%c + progress.width = 8 + progress.done = = + progress.pending = + progress.spinner = -\|/ + progress.sprites = loading ''' - prog_config = get_progress_config(uzbl) - config = uzbl.get_config() + global UPDATES - if prog is None: - prog = prog_config['progress'] + if progress is None: + UPDATES = 0 + progress = 100 else: - prog = int(prog) - prog_config['progress'] = prog + UPDATES += 1 + progress = int(progress) - prog_config['updates'] += 1 - format = prog_config['format'] - width = prog_config['width'] + # Get progress config vars. + format = uzbl.config.get('progress.format', '[%d>%p]%c') + width = int(uzbl.config.get('progress.width', 8)) + done_symbol = uzbl.config.get('progress.done', '=') + pend = uzbl.config.get('progress.pending', None) + pending_symbol = pend if pend else ' ' # 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 + width += (3-len(str(progress))) * count if '%t' in format or '%o' in format: count = format.count('%t') + format.count('%o') - width += (3-len(str(100-prog))) * count + width += (3-len(str(100-progress))) * count - done = int(((prog/100.0)*width)+0.5) + done = int(((progress/100.0)*width)+0.5) pending = width - done if '%d' in format: - format = format.replace('%d', prog_config['done']*done) + format = format.replace('%d', done_symbol * done) if '%p' in format: - format = format.replace('%p', prog_config['pending']*pending) + format = format.replace('%p', pending_symbol * pending) if '%c' in format: - format = format.replace('%c', '%d%%' % prog) + format = format.replace('%c', '%d%%' % progress) if '%i' in format: - format = format.replace('%i', '%d' % prog) + format = format.replace('%i', '%d' % progress) if '%t' in format: - format = format.replace('%t', '%d%%' % (100-prog)) + format = format.replace('%t', '%d%%' % (100-progress)) if '%o' in format: - format = format.replace('%o', '%d' % (100-prog)) + format = format.replace('%o', '%d' % (100-progress)) 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) + spinner = uzbl.config.get('progress.spinner', '-\\|/') + index = 0 if progress == 100 else UPDATES % len(spinner) + spin = '\\\\' if spinner[index] == '\\' else spinner[index] + format = format.replace('%s', spin) if '%r' in format: - sprites = prog_config['sprites'] - sprites = '-' if not sprites else sprites - index = int(((prog/100.0)*len(sprites))+0.5)-1 + sprites = uzbl.config.get('progress.sprites', 'loading') + index = int(((progress/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 = - ''' - - 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) - + if uzbl.config.get('progress.output', None) != format: + uzbl.config['progress.output'] = format +# plugin init hook def init(uzbl): - # Event handling hooks. - uzbl.connect_dict({ - 'INSTANCE_EXIT': del_instance, - 'INSTANCE_START': add_instance, - 'LOAD_COMMIT': reset_progress, + connect_dict(uzbl, { + 'LOAD_COMMIT': lambda uzbl, uri: update_progress(uzbl), 'LOAD_PROGRESS': update_progress, - 'PROGRESS_CONFIG': progress_config, }) diff --git a/examples/data/scripts/uzbl-event-manager b/examples/data/scripts/uzbl-event-manager index 7fa4a09..75a1c13 100755 --- a/examples/data/scripts/uzbl-event-manager +++ b/examples/data/scripts/uzbl-event-manager @@ -1,7 +1,7 @@ #!/usr/bin/env python # Event Manager for Uzbl -# Copyright (c) 2009, Mason Larobina +# Copyright (c) 2009-2010, Mason Larobina # Copyright (c) 2009, Dieter Plaetinck # # This program is free software: you can redistribute it and/or modify @@ -26,21 +26,23 @@ Event manager for uzbl written in python. ''' +import atexit import imp +import logging import os -import sys -import re import socket -import pprint +import sys import time -import atexit -from select import select -from signal import signal, SIGTERM -from optparse import OptionParser -from traceback import print_exc +import weakref +from collections import defaultdict from functools import partial +from glob import glob from itertools import count - +from optparse import OptionParser +from select import select +from signal import signal, SIGTERM, SIGINT +from socket import socket, AF_UNIX, SOCK_STREAM +from traceback import format_exc def xdghome(key, default): '''Attempts to use the environ XDG_*_HOME paths if they exist otherwise @@ -52,11 +54,6 @@ def xdghome(key, default): return os.path.join(os.environ['HOME'], default) - -# ============================================================================ -# ::: Default configuration section :::::::::::::::::::::::::::::::::::::::::: -# ============================================================================ - # `make install` will put the correct value here for your system PREFIX = '/usr/local/' @@ -64,120 +61,34 @@ PREFIX = '/usr/local/' DATA_DIR = os.path.join(xdghome('DATA', '.local/share/'), 'uzbl/') CACHE_DIR = os.path.join(xdghome('CACHE', '.cache/'), 'uzbl/') -# Event manager config dictionary. This is not to be confused with the config -# dict that tracks variables in the uzbl instance. -CONFIG = { - 'verbose': False, - 'daemon_mode': True, - 'auto_close': False, - - 'plugins_load': [], - 'plugins_ignore': [], - - 'plugin_dirs': [os.path.join(DATA_DIR, 'plugins/'), - os.path.join(PREFIX, 'share/uzbl/examples/data/plugins/')], - - 'server_socket': os.path.join(CACHE_DIR, 'event_daemon'), - 'pid_file': os.path.join(CACHE_DIR, 'event_daemon.pid'), -} - -# ============================================================================ -# ::: End of configuration section ::::::::::::::::::::::::::::::::::::::::::: -# ============================================================================ - - # Define some globals. SCRIPTNAME = os.path.basename(sys.argv[0]) -FINDSPACES = re.compile("\s+") - - -class ArgumentError(Exception): - pass - - -def echo(msg): - '''Prints only if the verbose flag has been set.''' - - if CONFIG['verbose']: - sys.stdout.write("%s: %s\n" % (SCRIPTNAME, msg)) - - -def error(msg): - '''Prints error messages to stderr.''' - - sys.stderr.write("%s: error: %s\n" % (SCRIPTNAME, msg)) - - -def find_plugins(plugin_dirs): - '''Find all event manager plugins in the plugin dirs and return a - dictionary of {'plugin-name.py': '/full/path/to/plugin-name.py', ...}''' - - plugins = {} - - for plugin_dir in plugin_dirs: - plugin_dir = os.path.realpath(os.path.expandvars(plugin_dir)) - if not os.path.isdir(plugin_dir): - continue - - for filename in os.listdir(plugin_dir): - if not filename.lower().endswith('.py'): - continue - - path = os.path.join(plugin_dir, filename) - if not os.path.isfile(path): - continue - - if filename not in plugins: - plugins[filename] = plugin_dir - - return plugins +def get_exc(): + '''Format `format_exc` for logging.''' + return "\n%s" % format_exc().rstrip() -def load_plugins(plugin_dirs, load=None, ignore=None): - '''Load event manager plugins found in the plugin_dirs.''' +def expandpath(path): + '''Expand and realpath paths.''' + return os.path.realpath(os.path.expandvars(path)) - load = [] if load is None else load - ignore = [] if ignore is None else ignore - - # Find the plugins in the plugin_dirs. - found = find_plugins(plugin_dirs) - - if load: - # Ignore anything not in the load list. - for plugin in found.keys(): - if plugin not in load: - del found[plugin] - - if ignore: - # Ignore anything in the ignore list. - for plugin in found.keys(): - if plugin in ignore: - del found[plugin] - - # Print plugin list to be loaded. - pprint.pprint(found) - - loaded = {} - # Load all found plugins into the loaded dict. - for (filename, plugin_dir) in found.items(): - name = filename[:-3] - info = imp.find_module(name, [plugin_dir]) - plugin = imp.load_module(name, *info) - loaded[(plugin_dir, filename)] = plugin - - return loaded +def ascii(u): + '''Convert unicode strings into ascii for transmission over + ascii-only streams/sockets/devices.''' + return u.encode('utf-8') def daemonize(): '''Daemonize the process using the Stevens' double-fork magic.''' + logger.info('entering daemon mode') + try: if os.fork(): os._exit(0) except OSError: - print_exc() - sys.stderr.write("fork #1 failed") + logger.critical(get_exc()) sys.exit(1) os.chdir('/') @@ -189,8 +100,7 @@ def daemonize(): os._exit(0) except OSError: - print_exc() - sys.stderr.write("fork #2 failed") + logger.critical(get_exc()) sys.exit(1) if sys.stdout.isatty(): @@ -206,6 +116,8 @@ def daemonize(): os.dup2(stdout.fileno(), sys.stdout.fileno()) os.dup2(stderr.fileno(), sys.stderr.fileno()) + logger.info('entered daemon mode') + def make_dirs(path): '''Make all basedirs recursively as required.''' @@ -213,637 +125,840 @@ def make_dirs(path): try: dirname = os.path.dirname(path) if not os.path.isdir(dirname): + logger.debug('creating directories %r' % dirname) os.makedirs(dirname) except OSError: - print_exc() + logger.error(get_exc()) -def make_pid_file(pid_file): - '''Make pid file at given pid_file location.''' +class EventHandler(object): + '''Event handler class. Used to store args and kwargs which are merged + come time to call the callback with the event args and kwargs.''' - make_dirs(pid_file) - fileobj = open(pid_file, 'w') - fileobj.write('%d' % os.getpid()) - fileobj.close() + nextid = count().next + def __init__(self, plugin, event, callback, args, kwargs): + self.id = self.nextid() + self.plugin = plugin + self.event = event + self.callback = callback + self.args = args + self.kwargs = kwargs -def del_pid_file(pid_file): - '''Delete pid file at given pid_file location.''' - if os.path.isfile(pid_file): - os.remove(pid_file) + def __repr__(self): + elems = ['id=%d' % self.id, 'event=%s' % self.event, + 'callback=%r' % self.callback] + if self.args: + elems.append('args=%s' % repr(self.args)) -def get_pid(pid_file): - '''Read pid from pid_file.''' + if self.kwargs: + elems.append('kwargs=%s' % repr(self.kwargs)) - try: - fileobj = open(pid_file, 'r') - pid = int(fileobj.read()) - fileobj.close() - return pid + elems.append('plugin=%s' % self.plugin.name) + return u'' % ', '.join(elems) - except IOError, ValueError: - print_exc() - return None + def call(self, uzbl, *args, **kwargs): + '''Execute the handler function and merge argument lists.''' -def pid_running(pid): - '''Returns True if a process with the given pid is running.''' + args = args + self.args + kwargs = dict(self.kwargs.items() + kwargs.items()) + self.callback(uzbl, *args, **kwargs) - try: - os.kill(pid, 0) - except OSError: - return False +class Plugin(object): + '''Plugin module wrapper object.''' - else: - return True + # Special functions exported from the Plugin instance to the + # plugin namespace. + special_functions = ['require', 'export', 'export_dict', 'connect', + 'connect_dict', 'logger'] -def term_process(pid): - '''Send a SIGTERM signal to the process with the given pid.''' + def __init__(self, parent, name, path, plugin): + self.parent = parent + self.name = name + self.path = path + self.plugin = plugin + self.logger = get_logger('plugin.%s' % name) - if not pid_running(pid): - return False + # Weakrefs to all handlers created by this plugin + self.handlers = set([]) - os.kill(pid, SIGTERM) + # Plugins init hook + self.init = getattr(plugin, 'init') - start = time.time() - while True: - if not pid_running(pid): - return True + # Plugins optional after hook + after = getattr(plugin, 'after', None) + self.after = after if callable(after) else None - if time.time() - start > 5: - raise OSError('failed to stop process with pid: %d' % pid) + # Plugins optional cleanup hook + cleanup = getattr(plugin, 'cleanup', None) + self.cleanup = cleanup if callable(cleanup) else None - time.sleep(0.25) + # Export plugin's instance methods to plugin namespace + for attr in self.special_functions: + plugin.__dict__[attr] = getattr(self, attr) -def parse_msg(uzbl, msg): - '''Parse an incoming msg from a uzbl instance. All non-event messages - will be printed here and not be passed to the uzbl instance event - handler function.''' + def __repr__(self): + return u'' % self.plugin - if not msg: - return - cmd = FINDSPACES.split(msg, 3) - if not cmd or cmd[0] != 'EVENT': - # Not an event message. - print '---', msg.encode('utf-8') - return + def export(self, uzbl, attr, object, prepend=True): + '''Attach `object` to `uzbl` instance. This is the preferred method + of sharing functionality, functions, data and objects between + plugins. - while len(cmd) < 4: - cmd.append('') + If the object is callable you may wish to turn the callable object + in to a meta-instance-method by prepending `uzbl` to the call stack. + You can change this behaviour with the `prepend` argument. + ''' - event, args = cmd[2], cmd[3] - if not event: - return + assert attr not in uzbl.exports, "attr %r already exported by %r" %\ + (attr, uzbl.exports[attr][0]) - try: - uzbl.event(event, args) + prepend = True if prepend and callable(object) else False + uzbl.__dict__[attr] = partial(object, uzbl) if prepend else object + uzbl.exports[attr] = (self, object, prepend) + uzbl.logger.info('exported %r to %r by plugin %r, prepended %r' + % (object, 'uzbl.%s' % attr, self.name, prepend)) - except: - print_exc() + def export_dict(self, uzbl, exports): + for (attr, object) in exports.items(): + self.export(uzbl, attr, object) -class EventHandler(object): - nexthid = count().next + def find_handler(self, event, callback, args, kwargs): + '''Check if a handler with the identical callback and arguments + exists and return it.''' - def __init__(self, event, handler, *args, **kargs): - if not callable(handler): - raise ArgumentError("EventHandler object requires a callable " - "object function for the handler argument not: %r" % handler) + # Remove dead refs + self.handlers -= set(filter(lambda ref: not ref(), self.handlers)) - self.function = handler - self.args = args - self.kargs = kargs - self.event = event - self.hid = self.nexthid() + # Find existing identical handler + for handler in [ref() for ref in self.handlers]: + if handler.event == event and handler.callback == callback \ + and handler.args == args and handler.kwargs == kwargs: + return handler - def __repr__(self): - args = ["event=%s" % self.event, "hid=%d" % self.hid, - "function=%r" % self.function] + def connect(self, uzbl, event, callback, *args, **kwargs): + '''Create an event handler object which handles `event` events. - if self.args: - args.append(u"args=%r" % unicode(self.args)) + Arguments passed to the connect function (`args` and `kwargs`) are + stored in the handler object and merged with the event arguments + come handler execution. - if self.kargs: - args.append(u"kargs=%r" % unicode(self.kargs)) + All handler functions must behave like a `uzbl` instance-method (that + means `uzbl` is prepended to the callback call arguments).''' - return u"" % ', '.join(args) + # Sanitise and check event name + event = event.upper().strip() + assert event and ' ' not in event + assert callable(callback), 'callback must be callable' -class UzblInstance(object): + # Check if an identical handler already exists + handler = self.find_handler(event, callback, args, kwargs) + if not handler: + # Create a new handler + handler = EventHandler(self, event, callback, args, kwargs) + self.handlers.add(weakref.ref(handler)) + self.logger.info('new %r' % handler) - # Give all plugins access to the main config dict. - global_config = CONFIG + uzbl.handlers[event].append(handler) + uzbl.logger.info('connected %r' % handler) + return handler - def __init__(self, parent, client_socket): - # Internal variables. - self.exports = {} - self.handlers = {} - self.parent = parent - self.client_socket = client_socket + def connect_dict(self, uzbl, connects): + for (event, callback) in connects.items(): + self.connect(uzbl, event, callback) - self.depth = 0 - self.buffer = '' - self.pid = None - # Call the init function in every plugin. The init function in each - # plugin is where that plugin connects functions to events and exports - # functions to the uzbl object. - for plugin in self.parent['plugins'].values(): - try: - plugin.init(self) + def require(self, plugin): + '''Check that plugin with name `plugin` has been loaded. Use this to + ensure that your plugins dependencies have been met.''' - except: - raise + assert plugin in self.parent.plugins, self.logger.critical( + 'plugin %r required by plugin %r' (plugin, self.name)) - def send(self, msg): - '''Send a command to the uzbl instance via the socket file.''' +class Uzbl(object): + def __init__(self, parent, child_socket): + self.opts = opts + self.parent = parent + self.child_socket = child_socket + self.time = time.time() + self.pid = None + self.name = None - msg = msg.strip() - if self.client_socket: - print (u'%s<-- %s' % (' ' * self.depth, msg)).encode('utf-8') - self.client_socket.send(("%s\n" % msg).encode('utf-8')) - - else: - print (u'%s!-- %s' % (' ' * self.depth, msg)).encode('utf-8') - - - def export(self, attr, object, prepend=True): - '''Attach an object to the current class instance. This is the - preferred method of sharing functionality, functions and objects - between plugins. - - If the object is callable you may wish to turn the callable object in - to an "instance method call" by using the `functools.partial(..)` - tool to prepend the `self` object to the callable objects argument - list. - - Example session from a plugins POV: - >>> config_dict = {'foo': 'data..', 'bar': 'other data..'} - >>> uzbl.export('config', config_dict) - >>> uzbl.config is config_dict - True - >>> print uzbl.config['foo'] - data.. - >>> uzbl.export('get', lambda uzbl, key: uzbl.config[key]) - >>> print uzbl.get('bar') - other data.. - ''' + # Flag if the instance has raised the INSTANCE_START event. + self.instance_start = False - if prepend and callable(object): - object = partial(object, self) + # Use name "unknown" until name is discovered. + self.logger = get_logger('uzbl-instance[]') - self.__dict__.__setitem__(attr, object) + # Track plugin event handlers and exported functions. + self.exports = {} + self.handlers = defaultdict(list) + # Internal vars + self._depth = 0 + self._buffer = '' - def export_dict(self, export_dict): - '''Export multiple (attr, object)'s at once inside a dict of the - form `{attr1: object1, attr2: object2, ...}`.''' - for (attr, object) in export_dict.items(): - self.export(attr, object) + def __repr__(self): + return '' % ', '.join([ + 'pid=%s' % (self.pid if self.pid else "Unknown"), + 'name=%s' % ('%r' % self.name if self.name else "Unknown"), + 'uptime=%f' % (time.time()-self.time), + '%d exports' % len(self.exports.keys()), + '%d handlers' % sum([len(l) for l in self.handlers.values()])]) - def connect(self, event, handler, *args, **kargs): - '''Connect a uzbl event with a handler. Handlers can either be a - function or a uzbl command string.''' + def init_plugins(self): + '''Call the init and after hooks in all loaded plugins for this + instance.''' - event = event.upper().strip() - assert event and ' ' not in event + # Initialise each plugin with the current uzbl instance. + for plugin in self.parent.plugins.values(): + self.logger.debug('calling %r plugin init hook' % plugin.name) + plugin.init(self) - if event not in self.handlers.keys(): - self.handlers[event] = [] + # Allow plugins to use exported features of other plugins by calling an + # optional `after` function in the plugins namespace. + for plugin in self.parent.plugins.values(): + if plugin.after: + self.logger.debug('calling %r plugin after hook'%plugin.name) + plugin.after(self) - handlerobj = EventHandler(event, handler, *args, **kargs) - self.handlers[event].append(handlerobj) - print handlerobj + def send(self, msg): + '''Send a command to the uzbl instance via the child socket + instance.''' - def connect_dict(self, connect_dict): - '''Connect a dictionary comprising of {"EVENT_NAME": handler, ..} to - the event handler stack. + msg = msg.strip() + assert self.child_socket, "socket inactive" - If you need to supply args or kargs to an event use the normal connect - function.''' + if opts.print_events: + print ascii(u'%s<-- %s' % (' ' * self._depth, msg)) - for (event, handler) in connect_dict.items(): - self.connect(event, handler) + self.child_socket.send(ascii("%s\n" % msg)) - def remove_by_id(self, hid): - '''Remove connected event handler by unique handler id.''' + def read(self): + '''Read data from the child socket and pass lines to the parse_msg + function.''' - for (event, handlers) in self.handlers.items(): - for handler in list(handlers): - if hid != handler.hid: - continue + try: + raw = unicode(self.child_socket.recv(8192), 'utf-8', 'ignore') + if not raw: + self.logger.debug('read null byte') + return self.close() - echo("removed %r" % handler) - handlers.remove(handler) - return + except: + self.logger.error(get_exc()) + return self.close() - echo('unable to find & remove handler with id: %d' % hid) + lines = (self._buffer + raw).split('\n') + self._buffer = lines.pop() + for line in filter(None, map(unicode.strip, lines)): + try: + self.parse_msg(line.strip()) + + except: + self.logger.error(get_exc()) + self.logger.error('erroneous event: %r' % line) - 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 + def parse_msg(self, line): + '''Parse an incoming message from a uzbl instance. Event strings + will be parsed into `self.event(event, args)`.''' - echo('unable to find & remove handler: %r' % handler) + # Split by spaces (and fill missing with nulls) + elems = (line.split(' ', 3) + ['',]*3)[:4] + # Ignore non-event messages. + if elems[0] != 'EVENT': + logger.info('non-event message: %r' % line) + if opts.print_events: + print '--- %s' % ascii(line) + return - def exec_handler(self, handler, *args, **kargs): - '''Execute event handler function.''' + # Check event string elements + (name, event, args) = elems[1:] + assert name and event, 'event string missing elements' + if not self.name: + self.name = name + self.logger = get_logger('uzbl-instance%s' % name) + self.logger.info('found instance name %r' % name) - args += handler.args - kargs = dict(handler.kargs.items()+kargs.items()) - handler.function(self, *args, **kargs) + assert self.name == name, 'instance name mismatch' + + # Handle the event with the event handlers through the event method + self.event(event, args) def event(self, event, *args, **kargs): '''Raise an event.''' event = event.upper() - elems = [event,] - if args: elems.append(unicode(args)) - if kargs: elems.append(unicode(kargs)) - print (u'%s--> %s' % (' ' * self.depth, ' '.join(elems))).encode('utf-8') + + if not opts.daemon_mode and opts.print_events: + elems = [event,] + if args: elems.append(unicode(args)) + if kargs: elems.append(unicode(kargs)) + print ascii(u'%s--> %s' % (' ' * self._depth, ' '.join(elems))) if event == "INSTANCE_START" and args: + assert not self.instance_start, 'instance already started' + self.pid = int(args[0]) + self.logger.info('found instance pid %r' % self.pid) + + self.init_plugins() + + elif event == "INSTANCE_EXIT": + self.logger.info('uzbl instance exit') + self.close() if event not in self.handlers: return for handler in self.handlers[event]: - self.depth += 1 + self._depth += 1 try: - self.exec_handler(handler, *args, **kargs) + handler.call(self, *args, **kargs) except: - print_exc() + self.logger.error(get_exc()) + + self._depth -= 1 + - self.depth -= 1 + def close_connection(self, child_socket): + '''Close child socket and delete the uzbl instance created for that + child socket connection.''' def close(self): - '''Close the client socket and clean up.''' + '''Close the client socket and call the plugin cleanup hooks.''' + + self.logger.debug('called close method') + + # Remove self from parent uzbls dict. + if self.child_socket in self.parent.uzbls: + self.logger.debug('removing self from uzbls list') + del self.parent.uzbls[self.child_socket] try: - self.client_socket.close() + if self.child_socket: + self.logger.debug('closing child socket') + self.child_socket.close() except: - pass + self.logger.error(get_exc()) + + finally: + self.child_socket = None - for (name, plugin) in self.parent['plugins'].items(): - if hasattr(plugin, 'cleanup'): + # Call plugins cleanup hooks. + for plugin in self.parent.plugins.values(): + if plugin.cleanup: + self.logger.debug('calling %r plugin cleanup hook' + % plugin.name) plugin.cleanup(self) + logger.info('removed %r' % self) -class UzblEventDaemon(dict): - def __init__(self): - # Init variables and dict keys. - dict.__init__(self, {'uzbls': {}}) - self.running = None +class UzblEventDaemon(object): + def __init__(self): + self.opts = opts self.server_socket = None - self.socket_location = None + self._quit = False + + # Hold uzbl instances + # {child socket: Uzbl instance, ..} + self.uzbls = {} + + # Hold plugins + # {plugin name: Plugin instance, ..} + self.plugins = {} # Register that the event daemon server has started by creating the # pid file. - make_pid_file(CONFIG['pid_file']) + make_pid_file(opts.pid_file) # Register a function to clean up the socket and pid file on exit. atexit.register(self.quit) - # Make SIGTERM act orderly. - signal(SIGTERM, lambda signum, stack_frame: sys.exit(1)) + # Add signal handlers. + for sigint in [SIGTERM, SIGINT]: + signal(sigint, self.quit) - # Load plugins, first-build of the plugins may be a costly operation. - self['plugins'] = load_plugins(CONFIG['plugin_dirs'], - CONFIG['plugins_load'], CONFIG['plugins_ignore']) + # Load plugins into self.plugins + self.load_plugins(opts.plugins) - def _create_server_socket(self): - '''Create the event manager daemon socket for uzbl instance duplex - communication.''' + def load_plugins(self, plugins): + '''Load event manager plugins.''' - server_socket = CONFIG['server_socket'] - server_socket = os.path.realpath(os.path.expandvars(server_socket)) - self.socket_location = server_socket + for path in plugins: + logger.debug('loading plugin %r' % path) + (dir, file) = os.path.split(path) + name = file[:-3] if file.lower().endswith('.py') else file - # Delete socket if it exists. - if os.path.exists(server_socket): - os.remove(server_socket) + info = imp.find_module(name, [dir,]) + module = imp.load_module(name, *info) + assert callable(getattr(module, 'init', None)),\ + "plugin missing init function: %r" % module - self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.server_socket.bind(server_socket) - self.server_socket.listen(5) + logger.debug('creating plugin instance for %r plugin' % name) + plugin = Plugin(self, name, path, module) + self.plugins[name] = plugin + logger.info('new %r' % plugin) - def _close_server_socket(self): - '''Close and delete the server socket.''' + def create_server_socket(self): + '''Create the event manager daemon socket for uzbl instance duplex + communication.''' - try: - self.server_socket.close() - self.server_socket = None + # Close old socket. + self.close_server_socket() - if os.path.exists(self.socket_location): - os.remove(self.socket_location) + sock = socket(AF_UNIX, SOCK_STREAM) + sock.bind(opts.server_socket) + sock.listen(5) - except: - pass + self.server_socket = sock + logger.debug('bound server socket to %r' % opts.server_socket) def run(self): '''Main event daemon loop.''' - # Create event daemon socket. - self._create_server_socket() - echo('listening on: %s' % self.socket_location) + logger.debug('entering main loop') + + # Create and listen on the server socket + self.create_server_socket() - if CONFIG['daemon_mode']: - echo('entering daemon mode.') + if opts.daemon_mode: + # Daemonize the process daemonize() - # The pid has changed so update the pid file. - make_pid_file(CONFIG['pid_file']) - # Now listen for incoming connections and or data. - self.listen() + # Update the pid file + make_pid_file(opts.pid_file) + + try: + # Accept incoming connections and listen for incoming data + self.listen() + + except: + if not self._quit: + logger.critical(get_exc()) - # Clean up. + # Clean up and exit self.quit() + logger.debug('exiting main loop') + def listen(self): '''Accept incoming connections and constantly poll instance sockets for incoming data.''' - self.running = True - while self.running: + logger.info('listening on %r' % opts.server_socket) - sockets = [self.server_socket] + self['uzbls'].keys() + # Count accepted connections + connections = 0 - reads, _, errors = select(sockets, [], sockets, 1) + while (self.uzbls or not connections) or (not opts.auto_close): + socks = [self.server_socket] + self.uzbls.keys() + reads, _, errors = select(socks, [], socks, 1) if self.server_socket in reads: - self.accept_connection() reads.remove(self.server_socket) - for client in reads: - self.read_socket(client) + # Accept connection and create uzbl instance. + child_socket = self.server_socket.accept()[0] + self.uzbls[child_socket] = Uzbl(self, child_socket) + connections += 1 + + for uzbl in [self.uzbls[s] for s in reads]: + uzbl.read() + + for uzbl in [self.uzbls[s] for s in errors]: + uzbl.logger.error('socket read error') + uzbl.close() - for client in errors: - error('Unknown error on socket: %r' % client) - self.close_connection(client) + logger.info('auto closing') - def read_socket(self, client): - '''Read data from an instance socket and pass to the uzbl objects - event handler function.''' + def close_server_socket(self): + '''Close and delete the server socket.''' - uzbl = self['uzbls'][client] try: - raw = unicode(client.recv(8192), 'utf-8', 'ignore') + if self.server_socket: + logger.debug('closing server socket') + self.server_socket.close() + self.server_socket = None + + if os.path.exists(opts.server_socket): + logger.info('unlinking %r' % opts.server_socket) + os.unlink(opts.server_socket) except: - print_exc() - raw = None + logger.error(get_exc()) - if not raw: - # Read null byte, close socket. - return self.close_connection(client) - uzbl.buffer += raw - msgs = uzbl.buffer.split('\n') - uzbl.buffer = msgs.pop() + def quit(self, sigint=None, *args): + '''Close all instance socket objects, server socket and delete the + pid file.''' - for msg in msgs: - try: - parse_msg(uzbl, msg.strip()) + if sigint == SIGTERM: + logger.critical('caught SIGTERM, exiting') - except: - print_exc() + elif sigint == SIGINT: + logger.critical('caught SIGINT, exiting') + elif not self._quit: + logger.debug('shutting down event manager') - def accept_connection(self): - '''Accept incoming connection to the server socket.''' + self.close_server_socket() - client_socket = self.server_socket.accept()[0] + for uzbl in self.uzbls.values(): + uzbl.close() - uzbl = UzblInstance(self, client_socket) - self['uzbls'][client_socket] = uzbl + del_pid_file(opts.pid_file) + if not self._quit: + logger.info('event manager shut down') + self._quit = True - def close_connection(self, client): - '''Clean up after instance close.''' +def make_pid_file(pid_file): + '''Creates a pid file at `pid_file`, fails silently.''' + + try: + logger.debug('creating pid file %r' % pid_file) + make_dirs(pid_file) + pid = os.getpid() + fileobj = open(pid_file, 'w') + fileobj.write('%d' % pid) + fileobj.close() + logger.info('created pid file %r with pid %d' % (pid_file, pid)) + + except: + logger.error(get_exc()) + + +def del_pid_file(pid_file): + '''Deletes a pid file at `pid_file`, fails silently.''' + + if os.path.isfile(pid_file): try: - if client in self['uzbls']: - uzbl = self['uzbls'][client] - uzbl.close() - del self['uzbls'][client] + logger.debug('deleting pid file %r' % pid_file) + os.remove(pid_file) + logger.info('deleted pid file %r' % pid_file) except: - print_exc() + logger.error(get_exc()) - if not len(self['uzbls']) and CONFIG['auto_close']: - echo('auto closing event manager.') - self.running = False +def get_pid(pid_file): + '''Reads a pid from pid file `pid_file`, fails None.''' - def quit(self): - '''Close all instance socket objects, server socket and delete the - pid file.''' + try: + logger.debug('reading pid file %r' % pid_file) + fileobj = open(pid_file, 'r') + pid = int(fileobj.read()) + fileobj.close() + logger.info('read pid %d from pid file %r' % (pid, pid_file)) + return pid - echo('shutting down event manager.') + except (IOError, ValueError): + logger.error(get_exc()) + return None - for client in self['uzbls'].keys(): - self.close_connection(client) - echo('unlinking: %r' % self.socket_location) - self._close_server_socket() +def pid_running(pid): + '''Checks if a process with a pid `pid` is running.''' - echo('deleting pid file: %r' % CONFIG['pid_file']) - del_pid_file(CONFIG['pid_file']) + try: + os.kill(pid, 0) + except OSError: + return False + else: + return True + + +def term_process(pid): + '''Asks nicely then forces process with pid `pid` to exit.''' + + try: + logger.info('sending SIGTERM to process with pid %r' % pid) + os.kill(pid, SIGTERM) + + except OSError: + logger.error(get_exc()) + + logger.debug('waiting for process with pid %r to exit' % pid) + start = time.time() + while True: + if not pid_running(pid): + logger.debug('process with pid %d exit' % pid) + return True + + if (time.time()-start) > 5: + logger.warning('process with pid %d failed to exit' % pid) + logger.info('sending SIGKILL to process with pid %d' % pid) + try: + os.kill(pid, SIGKILL) + except: + logger.critical(get_exc()) + raise + + if (time.time()-start) > 10: + logger.critical('unable to kill process with pid %d' % pid) + raise OSError + + time.sleep(0.25) def stop_action(): '''Stop the event manager daemon.''' - pid_file = CONFIG['pid_file'] + pid_file = opts.pid_file if not os.path.isfile(pid_file): - return echo('no running daemon found.') + logger.error('could not find running event manager with pid file %r' + % opts.pid_file) + return - echo('found pid file: %r' % pid_file) pid = get_pid(pid_file) if not pid_running(pid): - echo('no process with pid: %d' % pid) - return os.remove(pid_file) + logger.debug('no process with pid %r' % pid) + del_pid_file(pid_file) + return - echo("terminating process with pid: %d" % pid) + logger.debug('terminating process with pid %r' % pid) term_process(pid) - if os.path.isfile(pid_file): - os.remove(pid_file) - - echo('stopped event daemon.') + del_pid_file(pid_file) + logger.info('stopped event manager process with pid %d' % pid) def start_action(): '''Start the event manager daemon.''' - pid_file = CONFIG['pid_file'] + pid_file = opts.pid_file if os.path.isfile(pid_file): - echo('found pid file: %r' % pid_file) pid = get_pid(pid_file) if pid_running(pid): - return echo('event daemon already started with pid: %d' % pid) + logger.error('event manager already started with pid %d' % pid) + return - echo('no process with pid: %d' % pid) - os.remove(pid_file) + logger.info('no process with pid %d' % pid) + del_pid_file(pid_file) - echo('starting event manager.') UzblEventDaemon().run() def restart_action(): '''Restart the event manager daemon.''' - echo('restarting event manager daemon.') stop_action() start_action() def list_action(): - '''List all the plugins being loaded by the event daemon.''' + '''List all the plugins that would be loaded in the current search + dirs.''' - plugins = find_plugins(CONFIG['plugin_dirs']) - dirs = {} + names = {} + for plugin in opts.plugins: + (head, tail) = os.path.split(plugin) + if tail not in names: + names[tail] = plugin - for (plugin, plugin_dir) in plugins.items(): - if plugin_dir not in dirs: - dirs[plugin_dir] = [] + for plugin in sorted(names.values()): + print plugin - dirs[plugin_dir].append(plugin) - for (index, (plugin_dir, plugin_list)) in enumerate(sorted(dirs.items())): - if index: - print +if __name__ == "__main__": + parser = OptionParser('usage: %prog [options] {start|stop|restart|list}') + add = parser.add_option - print "%s:" % plugin_dir - for plugin in sorted(plugin_list): - print " %s" % plugin + add('-v', '--verbose', + dest='verbose', default=2, action='count', + help='increase verbosity') + add('-d', '--plugin-dir', + dest='plugin_dirs', action='append', metavar="DIR", default=[], + help='add extra plugin search dir, same as `-l "DIR/*.py"`') -if __name__ == "__main__": - USAGE = "usage: %prog [options] {start|stop|restart|list}" - PARSER = OptionParser(usage=USAGE) - PARSER.add_option('-v', '--verbose', dest='verbose', action="store_true", - help="print verbose output.") + add('-l', '--load-plugin', + dest='load_plugins', action='append', metavar="PLUGIN", default=[], + help='load plugin, loads before plugins in search dirs') - PARSER.add_option('-d', '--plugin-dirs', dest='plugin_dirs', action="store", - metavar="DIRS", help="Specify plugin directories in the form of "\ - "'dir1:dir2:dir3'.") + socket_location = os.path.join(CACHE_DIR, 'event_daemon') + add('-s', '--server-socket', + dest='server_socket', metavar="SOCKET", default=socket_location, + help='server AF_UNIX socket location') - PARSER.add_option('-l', '--load-plugins', dest="load", action="store", - metavar="PLUGINS", help="comma separated list of plugins to load") + add('-p', '--pid-file', + metavar="FILE", dest='pid_file', + help='pid file location, defaults to server socket + .pid') - PARSER.add_option('-i', '--ignore-plugins', dest="ignore", action="store", - metavar="PLUGINS", help="comma separated list of plugins to ignore") + add('-n', '--no-daemon', + dest='daemon_mode', action='store_false', default=True, + help='daemonize the process') - PARSER.add_option('-p', '--pid-file', dest='pid', action='store', - metavar='FILE', help="specify pid file location") + add('-a', '--auto-close', + dest='auto_close', action='store_true', default=False, + help='auto close after all instances disconnect') - PARSER.add_option('-s', '--server-socket', dest='socket', action='store', - metavar='SOCKET', help="specify the daemon socket location") + add('-i', '--no-default-dirs', + dest='default_dirs', action='store_false', default=True, + help='ignore the default plugin search dirs') - PARSER.add_option('-n', '--no-daemon', dest="daemon", - action="store_true", help="don't enter daemon mode.") + add('-o', '--log-file', + dest='log_file', metavar='FILE', + help='write logging output to a file, defaults to server socket +' + ' .log') - PARSER.add_option('-a', '--auto-close', dest='autoclose', - action='store_true', help='auto close after all instances disconnect.') + add('-q', '--quiet-events', + dest='print_events', action="store_false", default=True, + help="silence the printing of events to stdout") - (OPTIONS, ARGS) = PARSER.parse_args() + (opts, args) = parser.parse_args() - # init like {start|stop|..} daemon actions dict. - DAEMON_ACTIONS = {'start': start_action, 'stop': stop_action, - 'restart': restart_action, 'list': list_action} + opts.server_socket = expandpath(opts.server_socket) + + # Set default pid file location + if not opts.pid_file: + opts.pid_file = "%s.pid" % opts.server_socket - if not ARGS: - ACTION = 'start' + else: + opts.pid_file = expandpath(opts.pid_file) - elif len(ARGS) == 1: - ACTION = ARGS[0] - if ACTION not in DAEMON_ACTIONS: - raise ArgumentError("unknown argument: %r" % ACTION) + # Set default log file location + if not opts.log_file: + opts.log_file = "%s.log" % opts.server_socket else: - raise ArgumentError("too many arguments: %r" % ARGS) + opts.log_file = expandpath(opts.log_file) + + # Logging setup + log_level = logging.CRITICAL - opts.verbose*10 + + # Console logging handler + ch = logging.StreamHandler() + ch.setLevel(max(log_level+10, 10)) + ch.setFormatter(logging.Formatter( + '%(name)s: %(levelname)s: %(message)s')) + + # File logging handler + fh = logging.FileHandler(opts.log_file, 'w', 'utf-8', 1) + fh.setLevel(max(log_level, 10)) + fh.setFormatter(logging.Formatter( + '[%(created)f] %(name)s: %(levelname)s: %(message)s')) + + # logging.getLogger wrapper which sets the levels and adds the + # file and console handlers automagically + def get_logger(name): + handlers = [ch, fh] + level = [max(log_level, 10),] + logger = logging.getLogger(name) + logger.setLevel(level[0]) + for handler in handlers: + logger.addHandler(handler) + + return logger + + # Get main logger + logger = get_logger(SCRIPTNAME) + logger.info('logging to %r' % opts.log_file) - # parse other flags & options. - if OPTIONS.verbose: - CONFIG['verbose'] = True + plugins = {} - if OPTIONS.plugin_dirs: - PLUGIN_DIRS = [] - for DIR in OPTIONS.plugin_dirs.split(':'): - if not DIR: - continue + # Load all `opts.load_plugins` into the plugins list + for path in opts.load_plugins: + path = expandpath(path) + matches = glob(path) + if not matches: + parser.error('cannot find plugin(s): %r' % path) - PLUGIN_DIRS.append(os.path.realpath(DIR)) + for plugin in matches: + (head, tail) = os.path.split(plugin) + if tail not in plugins: + logger.debug('found plugin: %r' % plugin) + plugins[tail] = plugin - CONFIG['plugin_dirs'] = PLUGIN_DIRS - echo("plugin search dirs: %r" % PLUGIN_DIRS) + else: + logger.debug('ignoring plugin: %r' % plugin) - if OPTIONS.load and OPTIONS.ignore: - error("you can't load and ignore at the same time.") - sys.exit(1) + # Add default plugin locations + if opts.default_dirs: + logger.debug('adding default plugin dirs to plugin dirs list') + opts.plugin_dirs += [os.path.join(DATA_DIR, 'plugins/'), + os.path.join(PREFIX, 'share/uzbl/examples/data/plugins/')] + + else: + logger.debug('ignoring default plugin dirs') - elif OPTIONS.load: - LOAD = CONFIG['plugins_load'] - for PLUGIN in OPTIONS.load.split(','): - if PLUGIN.strip(): - LOAD.append(PLUGIN.strip()) + # Load all plugins in `opts.plugin_dirs` into the plugins list + for dir in opts.plugin_dirs: + dir = expandpath(dir) + logger.debug('searching plugin dir: %r' % dir) + for plugin in glob(os.path.join(dir, '*.py')): + (head, tail) = os.path.split(plugin) + if tail not in plugins: + logger.debug('found plugin: %r' % plugin) + plugins[tail] = plugin - echo('only loading plugin(s): %s' % ', '.join(LOAD)) + else: + logger.debug('ignoring plugin: %r' % plugin) - elif OPTIONS.ignore: - IGNORE = CONFIG['plugins_ignore'] - for PLUGIN in OPTIONS.ignore.split(','): - if PLUGIN.strip(): - IGNORE.append(PLUGIN.strip()) + plugins = plugins.values() - echo('ignoring plugin(s): %s' % ', '.join(IGNORE)) + # Check all the paths in the plugins list are files + for plugin in plugins: + if not os.path.isfile(plugin): + parser.error('plugin not a file: %r' % plugin) - if OPTIONS.autoclose: - CONFIG['auto_close'] = True - echo('will auto close.') + if opts.auto_close: logger.debug('will auto close') + else: logger.debug('will not auto close') - if OPTIONS.pid: - CONFIG['pid_file'] = os.path.realpath(OPTIONS.pid) - echo("pid file location: %r" % CONFIG['pid_file']) + if opts.daemon_mode: logger.debug('will daemonize') + else: logger.debug('will not daemonize') - if OPTIONS.socket: - CONFIG['server_socket'] = os.path.realpath(OPTIONS.socket) - echo("daemon socket location: %s" % CONFIG['server_socket']) + opts.plugins = plugins + + # init like {start|stop|..} daemon actions + daemon_actions = {'start': start_action, 'stop': stop_action, + 'restart': restart_action, 'list': list_action} + + if len(args) == 1: + action = args[0] + if action not in daemon_actions: + parser.error('invalid action: %r' % action) + + elif not args: + logger.warning('no daemon action given, assuming %r' % 'start') + action = 'start' + + else: + parser.error('invalid action argument: %r' % args) - if OPTIONS.daemon: - CONFIG['daemon_mode'] = False + logger.info('daemon action %r' % action) + # Do action + daemon_actions[action]() - # Now {start|stop|...} - DAEMON_ACTIONS[ACTION]() + logger.debug('process CPU time: %f' % time.clock()) -- cgit v1.2.3 From 9decf33a90ce6cfe998414705b8e75308cc13cc6 Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Sun, 4 Apr 2010 13:31:20 +0200 Subject: set 'iskeyword' local to buffer This setting broke most of the syntax highlighting of files opened after the uzbl config file. --- extras/vim/syntax/uzbl.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index eacbb7c..ab78a4b 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -26,7 +26,7 @@ elseif exists("b:current_syntax") endif " Don't match keywords inside strings -set iskeyword=!-~,192-255 +setl iskeyword=!-~,192-255 syn keyword uzblKeyword back forward scroll reload reload_ign_cache stop syn keyword uzblKeyword zoom_in zoom_out toggle_zoom_type uri script -- cgit v1.2.3 From 53101b79b2e59e42c1542210fdf54ce5254de15b Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Sun, 4 Apr 2010 13:39:55 +0200 Subject: add vim ftplugin file for uzbl config --- extras/vim/ftplugin/uzbl.vim | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 extras/vim/ftplugin/uzbl.vim diff --git a/extras/vim/ftplugin/uzbl.vim b/extras/vim/ftplugin/uzbl.vim new file mode 100644 index 0000000..513eaa4 --- /dev/null +++ b/extras/vim/ftplugin/uzbl.vim @@ -0,0 +1,29 @@ +" Vim filetype file +" Filename: uzbl.vim +" Maintainer: Gregor Uhlenheuer +" Last Change: Sun 04 Apr 2010 01:37:49 PM CEST + +if exists('b:did_ftplugin') + finish +endif + +let b:did_ftplugin = 1 + +" enable syntax based folding +setlocal foldmethod=syntax + +" correctly format comments +setlocal formatoptions=croql +setlocal comments=:# +setlocal commentstring=#%s + +" define config testing commands and mappings +if executable('uzbl-core') + com! -buffer UzblCoreTest !uzbl-core -c % + nmap uc :UzblCoreTest +endif + +if executable('uzbl-browser') + com! -buffer UzblBrowserTest !uzbl-browser -c % + nmap ub :UzblBrowserTest +endif -- cgit v1.2.3 From 49b32de5e537009e718b7422404e984bad6be007 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Sun, 4 Apr 2010 20:17:30 +0800 Subject: Require plugins have either a init, after or cleanup hook. --- examples/data/scripts/uzbl-event-manager | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/data/scripts/uzbl-event-manager b/examples/data/scripts/uzbl-event-manager index 75a1c13..ab13fbb 100755 --- a/examples/data/scripts/uzbl-event-manager +++ b/examples/data/scripts/uzbl-event-manager @@ -189,7 +189,8 @@ class Plugin(object): self.handlers = set([]) # Plugins init hook - self.init = getattr(plugin, 'init') + init = getattr(plugin, 'init', None) + self.init = init if callable(init) else None # Plugins optional after hook after = getattr(plugin, 'after', None) @@ -199,6 +200,8 @@ class Plugin(object): cleanup = getattr(plugin, 'cleanup', None) self.cleanup = cleanup if callable(cleanup) else None + assert init or after or cleanup, "missing hooks in plugin" + # Export plugin's instance methods to plugin namespace for attr in self.special_functions: plugin.__dict__[attr] = getattr(self, attr) @@ -328,8 +331,9 @@ class Uzbl(object): # Initialise each plugin with the current uzbl instance. for plugin in self.parent.plugins.values(): - self.logger.debug('calling %r plugin init hook' % plugin.name) - plugin.init(self) + if plugin.init: + self.logger.debug('calling %r plugin init hook' % plugin.name) + plugin.init(self) # Allow plugins to use exported features of other plugins by calling an # optional `after` function in the plugins namespace. @@ -518,8 +522,11 @@ class UzblEventDaemon(object): info = imp.find_module(name, [dir,]) module = imp.load_module(name, *info) - assert callable(getattr(module, 'init', None)),\ - "plugin missing init function: %r" % module + + # Check if the plugin has a callable hook. + hooks = filter(callable, [getattr(module, attr, None) \ + for attr in ['init', 'after', 'cleanup']]) + assert hooks, "no hooks in plugin %r" % module logger.debug('creating plugin instance for %r plugin' % name) plugin = Plugin(self, name, path, module) -- cgit v1.2.3 From 774e48054149fe1f9512104a2f821e0055168d8a Mon Sep 17 00:00:00 2001 From: Gregor Uhlenheuer Date: Sun, 4 Apr 2010 22:13:18 +0800 Subject: vim ftplugin for uzbl, config section folding & improved vim syntax file --- AUTHORS | 2 +- examples/config/config | 2 ++ extras/vim/ftplugin/uzbl.vim | 29 +++++++++++++++++++++++++++++ extras/vim/syntax/uzbl.vim | 14 +++++++------- 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 extras/vim/ftplugin/uzbl.vim diff --git a/AUTHORS b/AUTHORS index 7b2522c..43b2d88 100644 --- a/AUTHORS +++ b/AUTHORS @@ -39,7 +39,7 @@ In alphabetical order: Dmytro Milinevskyy - uzbl-tabbed useability patches Dusan Popovic (dusanx) - many contributions to early uzbl Evgeny Grablyk - libsoup settings - Gregor Uhlenheuer (kongo2002) - uzbl vim syntax + Gregor Uhlenheuer (kongo2002) - uzbl vim syntax & related files Helmut Grohne (helmut) - move void **ptr to union, various fixes Henri Kemppainen (DuClare) - many contributions, mostly old handler code Igor Bogomazov - mouse ptr events diff --git a/examples/config/config b/examples/config/config index 504c7b8..f95c7fc 100644 --- a/examples/config/config +++ b/examples/config/config @@ -387,3 +387,5 @@ set default_mode = command # Set the "home" page. set uri = uzbl.org/doesitwork/@COMMIT + +# vim: set fdm=syntax: diff --git a/extras/vim/ftplugin/uzbl.vim b/extras/vim/ftplugin/uzbl.vim new file mode 100644 index 0000000..513eaa4 --- /dev/null +++ b/extras/vim/ftplugin/uzbl.vim @@ -0,0 +1,29 @@ +" Vim filetype file +" Filename: uzbl.vim +" Maintainer: Gregor Uhlenheuer +" Last Change: Sun 04 Apr 2010 01:37:49 PM CEST + +if exists('b:did_ftplugin') + finish +endif + +let b:did_ftplugin = 1 + +" enable syntax based folding +setlocal foldmethod=syntax + +" correctly format comments +setlocal formatoptions=croql +setlocal comments=:# +setlocal commentstring=#%s + +" define config testing commands and mappings +if executable('uzbl-core') + com! -buffer UzblCoreTest !uzbl-core -c % + nmap uc :UzblCoreTest +endif + +if executable('uzbl-browser') + com! -buffer UzblBrowserTest !uzbl-browser -c % + nmap ub :UzblBrowserTest +endif diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index 39f2495..ab78a4b 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -26,7 +26,7 @@ elseif exists("b:current_syntax") endif " Don't match keywords inside strings -set iskeyword=!-~,192-255 +setl iskeyword=!-~,192-255 syn keyword uzblKeyword back forward scroll reload reload_ign_cache stop syn keyword uzblKeyword zoom_in zoom_out toggle_zoom_type uri script @@ -44,13 +44,13 @@ syn match uzblTodo /TODO:/ contained syn region uzblComment display start=/^#/ end=/$/ contains=uzblTodo " Comment headings -syn region uzblSection display start=/^# ===/ end=/$/ -syn region uzblSubSection display start=/^# ---/ end=/$/ +syn region uzblSec matchgroup=uzblSection start=/^# ===.*$/ end=/^# ===/me=e-5 contains=ALL fold +syn region uzblSSec matchgroup=uzblSubSection start=/^# ---.*$/ end=/^# [=-]\{3}/me=e-5 contains=ALLBUT,uzblSec,uzblSSec fold " Integer and float matching -syn match uzblPercent display /\s\(+\|-\|\)\(\d\+.\d\+\|\d\+\)%\(\s\|\n\)/ -syn match uzblInt display /\s\(+\|-\|\)\d\+\(\s\|\n\)/ -syn match uzblFloat display /\s\(+\|-\|\)\d\+.\d\+\(\s\|\n\)/ +syn match uzblPercent display /\s[+-]\=\%(\d\+\.\)\=\d\+%\_s/ +syn match uzblInt display /\s[+-]\=\d\+\_s/ +syn match uzblFloat display /\s[+-]\=\d\+\.\d\+\_s/ " Handler arguments syn match uzblArgs display /$\d\+/ @@ -63,7 +63,7 @@ syn match uzblInternalExpand display /@[A-Z_]\+/ syn match uzblInternalExpand display /@{[A-Z_]\+}/ " Matches $ENVIRON_VAR -syn match uzblEnvironVariable display /$[A-Za-z0-9_]\+/ +syn match uzblEnvironVariable display /$\a\+\w*/ " Matches @some_var and @{some_var} syn match uzblExpand display /@[A-Za-z0-9_\.]\+/ -- cgit v1.2.3 From e8a245d6e9b5f3cb5f5125c1e0f87a65ec0536b7 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Sun, 4 Apr 2010 21:54:15 +0800 Subject: Re-categorise parts of the config to fit under less folded sections --- examples/config/config | 171 ++++++++++++++++++++------------------------- extras/vim/syntax/uzbl.vim | 2 +- 2 files changed, 78 insertions(+), 95 deletions(-) diff --git a/examples/config/config b/examples/config/config index f95c7fc..4c8b6b3 100644 --- a/examples/config/config +++ b/examples/config/config @@ -1,15 +1,20 @@ -# example uzbl config. -# all settings are optional. you can use uzbl without any config at all (but it won't do much) +# Example uzbl config. All settings are optional. You can use uzbl without +# any config at all (but it won't do much). +# === Core settings ========================================================== + +# Install location prefix. set prefix = /usr/local -# === Shortcuts / Aliases =================================================== +# Interface paths. +set fifo_dir = /tmp +set socket_dir = /tmp + +set shell_cmd = sh -c + +# === General config aliases ================================================= # Config related events (use the request function): -# request BIND = -set bind = request BIND -# request MODE_BIND = -set mode_bind = request MODE_BIND # request MODE_CONFIG = set mode_config = request MODE_CONFIG # request ON_EVENT @@ -22,22 +27,16 @@ set modmap = request MODMAP set ignore_key = request IGNORE_KEY # request MODKEY_ADDITION set modkey_addition = request MODKEY_ADDITION - -# Action related events (use the event function): -# event TOGGLE_MODES ... -set toggle_modes = event TOGGLE_MODES +# request TOGGLE_MODES ... +set toggle_modes = request TOGGLE_MODES 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:@prefix/share/uzbl/examples/data:scripts - -# === Handlers =============================================================== - -# --- Hardcoded event handlers ----------------------------------------------- +# === Hardcoded handlers ===================================================== # These handlers can't be moved to the new event system yet as we don't # support events that can wait for a response from a script. @@ -45,7 +44,7 @@ set cookie_handler = talk_to_socket $XDG_CACHE_HOME/uzbl/cookie_daemon_sock set scheme_handler = sync_spawn @scripts_dir/scheme.py set authentication_handler = sync_spawn @scripts_dir/auth.py -# --- Optional dynamic event handlers ---------------------------------------- +# === Dynamic event handlers ================================================= # Open link in new window @on_event NEW_WINDOW sh 'uzbl-browser -u "$8"' %r @@ -77,7 +76,6 @@ set authentication_handler = sync_spawn @scripts_dir/auth.py # Example CONFIG_CHANGED event handler #@on_event CONFIG_CHANGED print Config changed: %1 = %2 - # === Behaviour and appearance =============================================== set show_status = 1 @@ -112,21 +110,17 @@ set title_format_long = \@keycmd_prompt \@raw_modcmd \@raw_keycmd \@TITLE - Uzbl @progress done = = @progress pending = -# Or ride those spinnas' -#@progress format = [%d%s%p] -#@progress spinner = -\\|/ -#@progress done = - -#@progress pending = +# === Useragent setup ======================================================== +# Useragent components. +set engine_ident = Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}.@{WEBKIT_MICRO} +set arch_ident = @(+uname -s)@ @(+uname -m)@ [@ARCH_UZBL] +set version_ident = Commit @COMMIT -# === Core settings ========================================================== - -set useragent = Uzbl (Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}.@{WEBKIT_MICRO}) (@(+uname -s)@ @(+uname -m)@ [@ARCH_UZBL]) (Commit @COMMIT) -set fifo_dir = /tmp -set socket_dir = /tmp +set useragent = Uzbl (@engine_ident) (@arch_ident) (@version_ident) - -# === Key modmapping and ignoring ============================================ +# === Key binding configuration ============================================== +# --- Internal modmapping and ignoring --------------------------------------- #modmap @modmap @@ -142,23 +136,24 @@ set socket_dir = /tmp @ignore_key @ignore_key +# --- Bind aliases ----------------------------------------------------------- -# === Mode bind aliases ====================================================== +# request BIND = +set bind = request MODE_BIND global -# Global binding alias (this is done automatically inside the bind plugin). -#set bind = @mode_bind global +# request MODE_BIND = +set mode_bind = request MODE_BIND # Insert mode binding alias -set ibind = @mode_bind insert +set ibind = @mode_bind insert # Command mode binding alias -set cbind = @mode_bind command +set cbind = @mode_bind command # Non-insert mode bindings alias (ebind for edit-bind). -set ebind = @mode_bind global,-insert - +set ebind = @mode_bind global,-insert -# === Global & keycmd editing binds ========================================== +# --- Global & keycmd editing binds ------------------------------------------ # Resets keycmd and returns to default mode. @on_event ESCAPE @set_mode @@ -180,26 +175,18 @@ set ebind = @mode_bind global,-insert @ebind a = event SET_CURSOR_POS 0 @ebind e = event SET_CURSOR_POS -1 -# Keycmd injection/append examples. -#@ebind su = event INJECT_KEYCMD \@uri -#@ebind st = event INJECT_KEYCMD \@title -#@ebind du = event APPEND_KEYCMD \@uri -#@ebind dt = event APPEND_KEYCMD \@title - - -# === Mouse bindings ========================================================= +# --- Mouse bindings --------------------------------------------------------- # Middle click open in new window @bind = sh 'if [ "\@SELECTED_URI" ]; then uzbl-browser -u "\@SELECTED_URI"; else echo "uri $(xclip -o | sed s/\\\@/%40/g)" > $4; fi' - -# === Keyboard bindings ====================================================== +# --- Keyboard bindings ------------------------------------------------------ # With this command you can enter in any command at runtime when prefixed with # a colon. @cbind :_ = %s -# --- Page movement binds --- +# Page movement binds @cbind j = scroll vertical 20 @cbind k = scroll vertical -20 @cbind h = scroll horizontal -20 @@ -212,59 +199,36 @@ set ebind = @mode_bind global,-insert @cbind $ = scroll horizontal end @cbind = scroll vertical end -# --- Navigation binds --- +# Navigation binds @cbind b = back @cbind m = forward @cbind S = stop @cbind r = reload @cbind R = reload_ign_cache -# --- Zoom binds --- +# Zoom binds @cbind + = zoom_in @cbind - = zoom_out @cbind T = toggle_zoom_type @cbind 1 = set zoom_level = 1.0 @cbind 2 = set zoom_level = 2.0 -# --- Appearance binds --- +# Appearance binds @cbind t = toggle_status -# --- Page searching binds --- +# Page searching binds @cbind /* = search %s @cbind ?* = search_reverse %s # Jump to next and previous items @cbind n = search @cbind N = search_reverse -# --- Uzbl tabbed binds --- -# Tab opening -@cbind gn = event NEW_TAB -@cbind gN = event NEW_TAB_NEXT -@cbind go_ = event NEW_TAB %s -@cbind gO_ = event NEW_TAB_NEXT %s -@cbind gY = sh 'echo "event NEW_TAB `xclip -selection primary -o | sed s/\\\@/%40/g`" > $4' -# Closing / resting -@cbind gC = exit -@cbind gQ = event CLEAN_TABS -# Tab navigating -@cbind g< = event FIRST_TAB -@cbind g> = event LAST_TAB -@cbind gt = event NEXT_TAB -@cbind gT = event PREV_TAB -@cbind gi_ = event GOTO_TAB %s -# Preset loading -set preset = event PRESET_TABS -@cbind gs_ = @preset save %s -@cbind glo_ = @preset load %s -@cbind gd_ = @preset del %s -@cbind gli = @preset list - -# --- Web searching binds --- +# Web searching binds @cbind gg_ = uri http://www.google.com/search?q=\@\@ @cbind \\awiki_ = uri http://wiki.archlinux.org/index.php/Special:Search?search=\@\@&go=Go @cbind \\wiki_ = uri http://en.wikipedia.org/w/index.php?title=Special:Search&search=\@\@&go=Go -# --- Handy binds --- +# Handy binds # Set function shortcut @cbind s__ = set %1 = %2 # Exit binding @@ -279,13 +243,13 @@ set preset = event PRESET_TABS @cbind t = sh 'xterm -e "socat unix-connect:$5 -"' #@cbind t = sh 'urxvt -e socat unix-connect:$5 -' -# --- Uri opening prompts --- +# Uri opening prompts @cbind o_ = uri %s # Or have it load the current uri into the keycmd for editing @cbind O_ = uri %s -# --- Mode setting binds --- -# Changing mode via set. +# Mode setting binds +# Changing mode via set. @cbind I = @set_mode insert # Or toggle between modes by raising the toggle event. set toggle_cmd_ins = @toggle_modes command insert @@ -293,10 +257,10 @@ set toggle_cmd_ins = @toggle_modes command insert # And the global toggle bind. @bind i = @toggle_cmd_ins -# --- Hard-bound bookmarks --- +# Hard-bound bookmarks @cbind gh = uri http://www.uzbl.org -# --- Yanking & pasting binds --- +# Yanking & pasting binds @cbind yu = sh 'echo -n $6 | xclip' @cbind yy = sh 'echo -n $7 | xclip' @@ -309,16 +273,16 @@ set toggle_cmd_ins = @toggle_modes command insert # paste primary selection into keycmd at the cursor position @bind = sh 'echo "event INJECT_KEYCMD `xclip -o | sed s/\\\@/%40/g`" > $4' -# --- Bookmark inserting binds --- +# Bookmark inserting binds @cbind b_ = sh 'echo -e "$6 %s" >> $XDG_DATA_HOME/uzbl/bookmarks' # Or use a script to insert a bookmark. @cbind B = spawn @scripts_dir/insert_bookmark.sh -# --- Bookmark/history loading --- +# Bookmark/history loading @cbind U = spawn @scripts_dir/load_url_from_history.sh @cbind u = spawn @scripts_dir/load_url_from_bookmarks.sh -# --- Link following (similar to vimperator and konqueror) --- +# Link following (similar to vimperator and konqueror) # Set custom keys you wish to use for navigation. Some common examples: set follow_hint_keys = 0123456789 #set follow_hint_keys = qwerty @@ -326,7 +290,7 @@ set follow_hint_keys = 0123456789 #set follow_hint_keys = thsnd-rcgmvwb/;789aefijkopquxyz234 @cbind fl* = script @scripts_dir/follow.js '@follow_hint_keys %s' -# --- Form filler binds --- +# Form filler binds # This script allows you to configure (per domain) values to fill in form # fields (eg login information) and to fill in these values automatically. # This implementation allows you to save multiple profiles for each form @@ -337,11 +301,32 @@ set formfiller = spawn @scripts_dir/formfiller.sh @cbind zn = @formfiller new @cbind zl = @formfiller load -# --- Examples --- -# Example showing how to use uzbl's fifo to execute a command. -#@bind X1 = sh 'echo "set zoom_level = 1.0" > "$4"' -#@bind X2 = sh 'echo "js alert (\\"This is sent by the shell via a fifo\\")" > "$4"' +# --- Uzbl tabbed binds ------------------------------------------------------ + +# Tab opening +@cbind gn = event NEW_TAB +@cbind gN = event NEW_TAB_NEXT +@cbind go_ = event NEW_TAB %s +@cbind gO_ = event NEW_TAB_NEXT %s +@cbind gY = sh 'echo "event NEW_TAB `xclip -selection primary -o | sed s/\\\@/%40/g`" > $4' + +# Closing / resting +@cbind gC = exit +@cbind gQ = event CLEAN_TABS + +# Tab navigating +@cbind g< = event FIRST_TAB +@cbind g> = event LAST_TAB +@cbind gt = event NEXT_TAB +@cbind gT = event PREV_TAB +@cbind gi_ = event GOTO_TAB %s +# Preset loading +set preset = event PRESET_TABS +@cbind gs_ = @preset save %s +@cbind glo_ = @preset load %s +@cbind gd_ = @preset del %s +@cbind gli = @preset list # === Context menu items ===================================================== @@ -354,7 +339,6 @@ menu_add Quit uzbl = exit # Link context menu menu_link_add Print Link = print \@SELECTED_URI - # === Mode configuration ===================================================== # Define some mode specific uzbl configurations. @@ -382,8 +366,7 @@ set stack = @mode_config stack set default_mode = command - -# === Post-load misc commands =============================================== +# === Post-load misc commands ================================================ # Set the "home" page. set uri = uzbl.org/doesitwork/@COMMIT diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index ab78a4b..b8572c9 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -106,7 +106,7 @@ if version >= 508 || !exists("did_uzbl_syn_inits") HiLink uzblComment Comment HiLink uzblTodo Todo - HiLink uzblSection Folded + HiLink uzblSection SpecialComment HiLink uzblSubSection SpecialComment HiLink uzblKeyword Keyword -- cgit v1.2.3 From 8cd65e828b211ab931ddc49cae6f023a86a4f742 Mon Sep 17 00:00:00 2001 From: Dieter Plaetinck Date: Mon, 5 Apr 2010 12:50:39 +0200 Subject: [ should be escape-bound, not ] --- examples/config/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/config/config b/examples/config/config index 504c7b8..a46ba13 100644 --- a/examples/config/config +++ b/examples/config/config @@ -163,7 +163,7 @@ set ebind = @mode_bind global,-insert # Resets keycmd and returns to default mode. @on_event ESCAPE @set_mode @bind = event ESCAPE -@bind ] = event ESCAPE +@bind [ = event ESCAPE # Commands for editing and traversing the keycmd. @ebind = event KEYCMD_EXEC_CURRENT -- cgit v1.2.3 From 85f0a757e138e3ed624bbaeaa39e4e71b0107b8e Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Wed, 7 Apr 2010 21:19:15 +0200 Subject: check if soup_cookie could be parsed successfully --- src/uzbl-core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/uzbl-core.c b/src/uzbl-core.c index 227e8c6..37e717f 100644 --- a/src/uzbl-core.c +++ b/src/uzbl-core.c @@ -2405,8 +2405,12 @@ void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data) while(p != NULL) { p = p + 1; soup_cookie = soup_cookie_parse((const char *) p, soup_uri); - if(soup_cookie->domain == NULL) soup_cookie->domain = soup_uri->host; - soup_cookie_jar_add_cookie(uzbl.net.soup_cookie_jar, soup_cookie); + if (soup_cookie) + { + if(soup_cookie->domain == NULL) + soup_cookie->domain = soup_uri->host; + soup_cookie_jar_add_cookie(uzbl.net.soup_cookie_jar, soup_cookie); + } p = strchr(p, ';'); } -- cgit v1.2.3 From 3061a8de31407f91b5fc1fcb34bdd684542b451e Mon Sep 17 00:00:00 2001 From: kongo2002 Date: Thu, 8 Apr 2010 02:03:00 +0200 Subject: fix code style/typo --- src/uzbl-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/uzbl-core.c b/src/uzbl-core.c index 37e717f..7be98c3 100644 --- a/src/uzbl-core.c +++ b/src/uzbl-core.c @@ -2398,15 +2398,14 @@ void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data) char *cookies = (char *) g_malloc(len+1); strncpy(cookies, uzbl.comm.sync_stdout, len+1); - /* Disconnect to avoid recusion */ + /* Disconnect to avoid recursion */ g_object_disconnect(G_OBJECT(uzbl.net.soup_cookie_jar), "any_signal", G_CALLBACK(save_cookies_js), NULL, NULL); p = cookies - 1; while(p != NULL) { p = p + 1; soup_cookie = soup_cookie_parse((const char *) p, soup_uri); - if (soup_cookie) - { + if (soup_cookie) { if(soup_cookie->domain == NULL) soup_cookie->domain = soup_uri->host; soup_cookie_jar_add_cookie(uzbl.net.soup_cookie_jar, soup_cookie); -- cgit v1.2.3 From cffd9488c644d77cee5a6bb3acd448b4ee43fce7 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Sun, 11 Apr 2010 21:31:56 +0800 Subject: TOGGLE_MODES event was removed, update insert mode binds in config. --- examples/config/config | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/config/config b/examples/config/config index f33f10c..3c40d18 100644 --- a/examples/config/config +++ b/examples/config/config @@ -244,13 +244,8 @@ set ebind = @mode_bind global,-insert @cbind O_ = uri %s # Mode setting binds -# Changing mode via set. -@cbind I = @set_mode insert -# Or toggle between modes by raising the toggle event. -set toggle_cmd_ins = @toggle_modes command insert -@cbind i = @toggle_cmd_ins -# And the global toggle bind. -@bind i = @toggle_cmd_ins +@cbind i = @set_mode insert +@bind i = @set_mode insert # Hard-bound bookmarks @cbind gh = uri http://www.uzbl.org -- cgit v1.2.3 From 08d4098f075b5dc8f2a174ec4e824b26fd81c110 Mon Sep 17 00:00:00 2001 From: Mason Larobina Date: Sun, 11 Apr 2010 21:36:39 +0800 Subject: Add to the config the default mode config that was in the mode plugin --- examples/config/config | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/config/config b/examples/config/config index 3c40d18..cff13ad 100644 --- a/examples/config/config +++ b/examples/config/config @@ -340,10 +340,16 @@ set stack = @mode_config stack @command keycmd_style = foreground="red" @command status_background = #202020 @command mode_indicator = Cmd +@command keycmd_events = 1 +@command forward_keys = 0 +@command modcmd_updates = 1 # Insert mode config. @insert status_background = #303030 @insert mode_indicator = Ins +@insert forward_keys = 1 +@insert keycmd_events = 0 +@insert modcmd_updates = 0 # Multi-stage-binding mode config. @stack keycmd_events = 1 -- cgit v1.2.3