1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
59 #include <sys/ioctl.h>
65 /* commandline arguments (set initial values for the state variables) */
67 GOptionEntry entries[] =
69 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
70 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
71 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
72 "Whether to print all messages or just errors.", NULL },
73 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
74 "Name of the current instance (defaults to Xorg window id)", "NAME" },
75 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
76 "Path to config file or '-' for stdin", "FILE" },
77 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
78 "Socket ID", "SOCKET" },
79 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
80 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
81 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
82 "Print the version and exit", NULL },
83 { NULL, 0, 0, 0, NULL, NULL, NULL }
86 /* associate command names to their properties */
87 typedef const struct {
88 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
89 the PTR() macro is kind of preventing this change at the moment. */
97 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
99 /* abbreviations to help keep the table's width humane */
100 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
101 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
106 } var_name_to_ptr[] = {
107 /* variable name pointer to variable in code type dump callback function */
108 /* ---------------------------------------------------------------------------------------------- */
109 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
110 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
111 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
112 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
113 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
114 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
115 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
116 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
117 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
118 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
119 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
120 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
121 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
122 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
123 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
124 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
125 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
126 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
127 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
128 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
129 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
130 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
131 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
132 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
133 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
134 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
135 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
136 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
137 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
138 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
139 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
140 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
141 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
142 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
143 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
144 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
145 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
146 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
147 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
148 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
150 /* exported WebKitWebSettings properties */
151 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
152 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
153 { "default_font_family", PTR_V(uzbl.behave.default_font_family, STR, 1, cmd_default_font_family)},
154 { "monospace_font_family", PTR_V(uzbl.behave.monospace_font_family, STR, 1, cmd_monospace_font_family)},
155 { "cursive_font_family", PTR_V(uzbl.behave.cursive_font_family, STR, 1, cmd_cursive_font_family)},
156 { "sans_serif_font_family", PTR_V(uzbl.behave.sans_serif_font_family, STR, 1, cmd_sans_serif_font_family)},
157 { "serif_font_family", PTR_V(uzbl.behave.serif_font_family, STR, 1, cmd_serif_font_family)},
158 { "fantasy_font_family", PTR_V(uzbl.behave.fantasy_font_family, STR, 1, cmd_fantasy_font_family)},
159 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
160 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
161 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
162 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
163 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
164 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
165 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
166 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
167 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
168 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
169 { "resizable_text_areas", PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
170 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
171 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
172 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
174 /* constants (not dumpable or writeable) */
175 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
176 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
177 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
178 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
179 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
180 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
181 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
182 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
183 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
184 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
185 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
187 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
188 }, *n2v_p = var_name_to_ptr;
195 { "SHIFT", GDK_SHIFT_MASK }, // shift
196 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
197 { "CONTROL", GDK_CONTROL_MASK }, // control
198 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
199 { "MOD2", GDK_MOD2_MASK }, // 5th mod
200 { "MOD3", GDK_MOD3_MASK }, // 6th mod
201 { "MOD4", GDK_MOD4_MASK }, // 7th mod
202 { "MOD5", GDK_MOD5_MASK }, // 8th mod
203 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
204 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
205 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
206 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
207 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
208 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
209 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
210 { "META", GDK_META_MASK }, // meta (since 2.10)
215 /* construct a hash from the var_name_to_ptr array for quick access */
217 make_var_to_name_hash() {
218 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
220 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
225 /* --- UTILITY FUNCTIONS --- */
226 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
228 get_exp_type(gchar *s) {
232 else if(*(s+1) == '{')
233 return EXP_BRACED_VAR;
234 else if(*(s+1) == '<')
236 else if(*(s+1) == '[')
239 return EXP_SIMPLE_VAR;
245 * recurse == 1: don't expand '@(command)@'
246 * recurse == 2: don't expand '@<java script>@'
249 expand(char *s, guint recurse) {
253 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
258 gchar *cmd_stdout = NULL;
260 GString *buf = g_string_new("");
261 GString *js_ret = g_string_new("");
266 g_string_append_c(buf, *++s);
271 etype = get_exp_type(s);
276 vend = strpbrk(s, end_simple_var);
277 if(!vend) vend = strchr(s, '\0');
281 vend = strchr(s, upto);
282 if(!vend) vend = strchr(s, '\0');
286 strcpy(str_end, ")@");
288 vend = strstr(s, str_end);
289 if(!vend) vend = strchr(s, '\0');
293 strcpy(str_end, ">@");
295 vend = strstr(s, str_end);
296 if(!vend) vend = strchr(s, '\0');
300 strcpy(str_end, "]@");
302 vend = strstr(s, str_end);
303 if(!vend) vend = strchr(s, '\0');
308 strncpy(ret, s, vend-s);
312 if(etype == EXP_SIMPLE_VAR ||
313 etype == EXP_BRACED_VAR) {
314 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
315 if(c->type == TYPE_STR && *c->ptr != NULL) {
316 g_string_append(buf, (gchar *)*c->ptr);
317 } else if(c->type == TYPE_INT) {
318 g_string_append_printf(buf, "%d", (int)*c->ptr);
320 else if(c->type == TYPE_FLOAT) {
321 g_string_append_printf(buf, "%f", *(float *)c->ptr);
325 if(etype == EXP_SIMPLE_VAR)
330 else if(recurse != 1 &&
332 mycmd = expand(ret, 1);
333 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
337 g_printerr("error on running command: %s\n", err->message);
340 else if (*cmd_stdout) {
341 int len = strlen(cmd_stdout);
343 if(cmd_stdout[len-1] == '\n')
344 cmd_stdout[--len] = 0; /* strip trailing newline */
346 g_string_append(buf, cmd_stdout);
351 else if(recurse != 2 &&
353 mycmd = expand(ret, 2);
354 eval_js(uzbl.gui.web_view, mycmd, js_ret);
358 g_string_append(buf, js_ret->str);
359 g_string_free(js_ret, TRUE);
360 js_ret = g_string_new("");
364 else if(etype == EXP_ESCAPE) {
365 mycmd = expand(ret, 0);
366 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
368 g_string_append(buf, escaped);
377 g_string_append_c(buf, *s);
382 g_string_free(js_ret, TRUE);
383 return g_string_free(buf, FALSE);
390 snprintf(tmp, sizeof(tmp), "%i", val);
391 return g_strdup(tmp);
395 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
398 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
401 str_replace (const char* search, const char* replace, const char* string) {
405 buf = g_strsplit (string, search, -1);
406 ret = g_strjoinv (replace, buf);
407 g_strfreev(buf); // somebody said this segfaults
413 read_file_by_line (gchar *path) {
414 GIOChannel *chan = NULL;
415 gchar *readbuf = NULL;
417 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
420 chan = g_io_channel_new_file(path, "r", NULL);
423 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
424 const gchar* val = g_strdup (readbuf);
425 g_array_append_val (lines, val);
430 g_io_channel_unref (chan);
432 fprintf(stderr, "File '%s' not be read.\n", path);
439 parseenv (char* string) {
440 extern char** environ;
441 gchar* tmpstr = NULL;
445 while (environ[i] != NULL) {
446 gchar** env = g_strsplit (environ[i], "=", 2);
447 gchar* envname = g_strconcat ("$", env[0], NULL);
449 if (g_strrstr (string, envname) != NULL) {
450 tmpstr = g_strdup(string);
452 string = str_replace(envname, env[1], tmpstr);
457 g_strfreev (env); // somebody said this breaks uzbl
465 setup_signal(int signr, sigfunc *shandler) {
466 struct sigaction nh, oh;
468 nh.sa_handler = shandler;
469 sigemptyset(&nh.sa_mask);
472 if(sigaction(signr, &nh, &oh) < 0)
480 if (uzbl.behave.fifo_dir)
481 unlink (uzbl.comm.fifo_path);
482 if (uzbl.behave.socket_dir)
483 unlink (uzbl.comm.socket_path);
485 g_free(uzbl.state.executable_path);
486 g_free(uzbl.state.keycmd);
487 g_hash_table_destroy(uzbl.bindings);
488 g_hash_table_destroy(uzbl.behave.commands);
491 /* used for html_mode_timeout
492 * be sure to extend this function to use
493 * more timers if needed in other places
496 set_timeout(int seconds) {
498 memset(&t, 0, sizeof t);
500 t.it_value.tv_sec = seconds;
501 t.it_value.tv_usec = 0;
502 setitimer(ITIMER_REAL, &t, NULL);
505 /* --- SIGNAL HANDLER --- */
508 catch_sigterm(int s) {
514 catch_sigint(int s) {
524 set_var_value("mode", "0");
529 /* --- CALLBACKS --- */
532 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
535 (void) navigation_action;
536 (void) policy_decision;
538 const gchar* uri = webkit_network_request_get_uri (request);
539 if (uzbl.state.verbose)
540 printf("New window requested -> %s \n", uri);
541 webkit_web_policy_decision_use(policy_decision);
546 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
551 /* If we can display it, let's display it... */
552 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
553 webkit_web_policy_decision_use (policy_decision);
557 /* ...everything we can't displayed is downloaded */
558 webkit_web_policy_decision_download (policy_decision);
563 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
567 if (uzbl.state.selected_url != NULL) {
568 if (uzbl.state.verbose)
569 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
570 new_window_load_uri(uzbl.state.selected_url);
572 if (uzbl.state.verbose)
573 printf("New web view -> %s\n","Nothing to open, exiting");
579 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
582 if (uzbl.behave.download_handler) {
583 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
584 if (uzbl.state.verbose)
585 printf("Download -> %s\n",uri);
586 /* if urls not escaped, we may have to escape and quote uri before this call */
587 run_handler(uzbl.behave.download_handler, uri);
592 /* scroll a bar in a given direction */
594 scroll (GtkAdjustment* bar, GArray *argv) {
598 gdouble page_size = gtk_adjustment_get_page_size(bar);
599 gdouble value = gtk_adjustment_get_value(bar);
600 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
603 value += page_size * amount * 0.01;
607 max_value = gtk_adjustment_get_upper(bar) - page_size;
609 if (value > max_value)
610 value = max_value; /* don't scroll past the end of the page */
612 gtk_adjustment_set_value (bar, value);
616 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) argv; (void) result;
618 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
622 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
623 (void) page; (void) argv; (void) result;
624 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
625 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
629 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
630 (void) page; (void) result;
631 scroll(uzbl.gui.bar_v, argv);
635 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
636 (void) page; (void) result;
637 scroll(uzbl.gui.bar_h, argv);
642 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
643 if(uzbl.state.verbose)
644 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
646 /* update geometry var with the actual geometry
647 this is necessary as some WMs don't seem to honour
648 the above setting and we don't want to end up with
649 wrong geometry information
656 if (!uzbl.behave.show_status) {
657 gtk_widget_hide(uzbl.gui.mainbar);
659 gtk_widget_show(uzbl.gui.mainbar);
665 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
670 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
674 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
679 if (uzbl.behave.show_status) {
680 gtk_widget_hide(uzbl.gui.mainbar);
682 gtk_widget_show(uzbl.gui.mainbar);
684 uzbl.behave.show_status = !uzbl.behave.show_status;
689 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
693 //Set selected_url state variable
694 g_free(uzbl.state.selected_url);
695 uzbl.state.selected_url = NULL;
697 uzbl.state.selected_url = g_strdup(link);
703 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
706 const gchar *title = webkit_web_view_get_title(web_view);
707 if (uzbl.gui.main_title)
708 g_free (uzbl.gui.main_title);
709 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
714 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
717 uzbl.gui.sbar.load_progress = progress;
719 g_free(uzbl.gui.sbar.progress_bar);
720 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
726 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
730 if (uzbl.behave.load_finish_handler)
731 run_handler(uzbl.behave.load_finish_handler, "");
734 void clear_keycmd() {
735 g_free(uzbl.state.keycmd);
736 uzbl.state.keycmd = g_strdup("");
740 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
744 uzbl.gui.sbar.load_progress = 0;
745 clear_keycmd(); // don't need old commands to remain on new page?
746 if (uzbl.behave.load_start_handler)
747 run_handler(uzbl.behave.load_start_handler, "");
751 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
754 g_free (uzbl.state.uri);
755 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
756 uzbl.state.uri = g_string_free (newuri, FALSE);
757 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
758 set_insert_mode(uzbl.behave.always_insert_mode);
761 if (uzbl.behave.load_commit_handler)
762 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
766 destroy_cb (GtkWidget* widget, gpointer data) {
774 if (uzbl.behave.history_handler) {
776 struct tm * timeinfo;
779 timeinfo = localtime ( &rawtime );
780 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
781 run_handler(uzbl.behave.history_handler, date);
786 /* VIEW funcs (little webkit wrappers) */
787 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
789 VIEWFUNC(reload_bypass_cache)
790 VIEWFUNC(stop_loading)
797 /* -- command to callback/function map for things we cannot attach to any signals */
798 struct {char *key; CommandInfo value;} cmdlist[] =
799 { /* key function no_split */
800 { "back", {view_go_back, 0} },
801 { "forward", {view_go_forward, 0} },
802 { "scroll_vert", {scroll_vert, 0} },
803 { "scroll_horz", {scroll_horz, 0} },
804 { "scroll_begin", {scroll_begin, 0} },
805 { "scroll_end", {scroll_end, 0} },
806 { "reload", {view_reload, 0}, },
807 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
808 { "stop", {view_stop_loading, 0}, },
809 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
810 { "zoom_out", {view_zoom_out, 0}, },
811 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
812 { "uri", {load_uri, TRUE} },
813 { "js", {run_js, TRUE} },
814 { "script", {run_external_js, 0} },
815 { "toggle_status", {toggle_status_cb, 0} },
816 { "spawn", {spawn, 0} },
817 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
818 { "sh", {spawn_sh, 0} },
819 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
820 { "talk_to_socket", {talk_to_socket, 0} },
821 { "exit", {close_uzbl, 0} },
822 { "search", {search_forward_text, TRUE} },
823 { "search_reverse", {search_reverse_text, TRUE} },
824 { "dehilight", {dehilight, 0} },
825 { "toggle_insert_mode", {toggle_insert_mode, 0} },
826 { "set", {set_var, TRUE} },
827 //{ "get", {get_var, TRUE} },
828 { "bind", {act_bind, TRUE} },
829 { "dump_config", {act_dump_config, 0} },
830 { "keycmd", {keycmd, TRUE} },
831 { "keycmd_nl", {keycmd_nl, TRUE} },
832 { "keycmd_bs", {keycmd_bs, 0} },
833 { "chain", {chain, 0} },
834 { "print", {print, TRUE} }
841 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
843 for (i = 0; i < LENGTH(cmdlist); i++)
844 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
847 /* -- CORE FUNCTIONS -- */
850 free_action(gpointer act) {
851 Action *action = (Action*)act;
852 g_free(action->name);
854 g_free(action->param);
859 new_action(const gchar *name, const gchar *param) {
860 Action *action = g_new(Action, 1);
862 action->name = g_strdup(name);
864 action->param = g_strdup(param);
866 action->param = NULL;
872 file_exists (const char * filename) {
873 return (access(filename, F_OK) == 0);
877 set_var(WebKitWebView *page, GArray *argv, GString *result) {
878 (void) page; (void) result;
879 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
880 if (split[0] != NULL) {
881 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
882 set_var_value(g_strstrip(split[0]), value);
889 print(WebKitWebView *page, GArray *argv, GString *result) {
890 (void) page; (void) result;
893 buf = expand(argv_idx(argv, 0), 0);
894 g_string_assign(result, buf);
899 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
900 (void) page; (void) result;
901 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
902 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
903 add_binding(g_strstrip(split[0]), value);
921 set_mode_indicator() {
922 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
923 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
928 set_mode_indicator();
933 set_insert_mode(gboolean mode) {
934 uzbl.behave.insert_mode = mode;
935 set_mode_indicator();
939 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
940 (void) page; (void) result;
942 if (argv_idx(argv, 0)) {
943 if (strcmp (argv_idx(argv, 0), "0") == 0) {
944 set_insert_mode(FALSE);
946 set_insert_mode(TRUE);
949 set_insert_mode( !uzbl.behave.insert_mode );
956 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
959 if (argv_idx(argv, 0)) {
960 GString* newuri = g_string_new (argv_idx(argv, 0));
961 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
962 run_js(web_view, argv, NULL);
965 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
966 g_string_prepend (newuri, "http://");
967 /* if we do handle cookies, ask our handler for them */
968 webkit_web_view_load_uri (web_view, newuri->str);
969 g_string_free (newuri, TRUE);
976 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
977 size_t argumentCount, const JSValueRef arguments[],
978 JSValueRef* exception) {
983 JSStringRef js_result_string;
984 GString *result = g_string_new("");
986 if (argumentCount >= 1) {
987 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
988 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
989 char ctl_line[arg_size];
990 JSStringGetUTF8CString(arg, ctl_line, arg_size);
992 parse_cmd_line(ctl_line, result);
994 JSStringRelease(arg);
996 js_result_string = JSStringCreateWithUTF8CString(result->str);
998 g_string_free(result, TRUE);
1000 return JSValueMakeString(ctx, js_result_string);
1003 JSStaticFunction js_static_functions[] = {
1004 {"run", js_run_command, kJSPropertyAttributeNone},
1009 /* This function creates the class and its definition, only once */
1010 if (!uzbl.js.initialized) {
1011 /* it would be pretty cool to make this dynamic */
1012 uzbl.js.classdef = kJSClassDefinitionEmpty;
1013 uzbl.js.classdef.staticFunctions = js_static_functions;
1015 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1021 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1022 WebKitWebFrame *frame;
1023 JSGlobalContextRef context;
1024 JSObjectRef globalobject;
1025 JSStringRef var_name;
1027 JSStringRef js_script;
1028 JSValueRef js_result;
1029 JSStringRef js_result_string;
1030 size_t js_result_size;
1034 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1035 context = webkit_web_frame_get_global_context(frame);
1036 globalobject = JSContextGetGlobalObject(context);
1038 /* uzbl javascript namespace */
1039 var_name = JSStringCreateWithUTF8CString("Uzbl");
1040 JSObjectSetProperty(context, globalobject, var_name,
1041 JSObjectMake(context, uzbl.js.classref, NULL),
1042 kJSClassAttributeNone, NULL);
1044 /* evaluate the script and get return value*/
1045 js_script = JSStringCreateWithUTF8CString(script);
1046 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1047 if (js_result && !JSValueIsUndefined(context, js_result)) {
1048 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1049 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1051 if (js_result_size) {
1052 char js_result_utf8[js_result_size];
1053 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1054 g_string_assign(result, js_result_utf8);
1057 JSStringRelease(js_result_string);
1061 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1063 JSStringRelease(var_name);
1064 JSStringRelease(js_script);
1068 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1069 if (argv_idx(argv, 0))
1070 eval_js(web_view, argv_idx(argv, 0), result);
1074 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1076 if (argv_idx(argv, 0)) {
1077 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1082 while ((line = g_array_index(lines, gchar*, i))) {
1084 js = g_strdup (line);
1086 gchar* newjs = g_strconcat (js, line, NULL);
1093 if (uzbl.state.verbose)
1094 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1096 if (argv_idx (argv, 1)) {
1097 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1101 eval_js (web_view, js, result);
1103 g_array_free (lines, TRUE);
1108 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1109 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1110 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1111 webkit_web_view_unmark_text_matches (page);
1112 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1113 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1117 if (uzbl.state.searchtx) {
1118 if (uzbl.state.verbose)
1119 printf ("Searching: %s\n", uzbl.state.searchtx);
1120 webkit_web_view_set_highlight_text_matches (page, TRUE);
1121 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1126 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1128 search_text(page, argv, TRUE);
1132 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1134 search_text(page, argv, FALSE);
1138 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1139 (void) argv; (void) result;
1140 webkit_web_view_set_highlight_text_matches (page, FALSE);
1145 new_window_load_uri (const gchar * uri) {
1146 if (uzbl.behave.new_window) {
1147 GString *s = g_string_new ("");
1148 g_string_printf(s, "'%s'", uri);
1149 run_handler(uzbl.behave.new_window, s->str);
1152 GString* to_execute = g_string_new ("");
1153 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1155 for (i = 0; entries[i].long_name != NULL; i++) {
1156 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1157 gchar** str = (gchar**)entries[i].arg_data;
1159 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1163 if (uzbl.state.verbose)
1164 printf("\n%s\n", to_execute->str);
1165 g_spawn_command_line_async (to_execute->str, NULL);
1166 g_string_free (to_execute, TRUE);
1170 chain (WebKitWebView *page, GArray *argv, GString *result) {
1171 (void) page; (void) result;
1173 gchar **parts = NULL;
1175 while ((a = argv_idx(argv, i++))) {
1176 parts = g_strsplit (a, " ", 2);
1178 parse_command(parts[0], parts[1], result);
1184 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1188 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1194 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1198 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1204 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1209 int len = strlen(uzbl.state.keycmd);
1210 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1212 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1217 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1224 /* --Statusbar functions-- */
1226 build_progressbar_ascii(int percent) {
1227 int width=uzbl.gui.sbar.progress_w;
1230 GString *bar = g_string_new("");
1232 l = (double)percent*((double)width/100.);
1233 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1235 for(i=0; i<(int)l; i++)
1236 g_string_append(bar, uzbl.gui.sbar.progress_s);
1239 g_string_append(bar, uzbl.gui.sbar.progress_u);
1241 return g_string_free(bar, FALSE);
1243 /* --End Statusbar functions-- */
1246 sharg_append(GArray *a, const gchar *str) {
1247 const gchar *s = (str ? str : "");
1248 g_array_append_val(a, s);
1251 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1253 run_command (const gchar *command, const guint npre, const gchar **args,
1254 const gboolean sync, char **output_stdout) {
1255 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1258 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1259 gchar *pid = itos(getpid());
1260 gchar *xwin = itos(uzbl.xwin);
1262 sharg_append(a, command);
1263 for (i = 0; i < npre; i++) /* add n args before the default vars */
1264 sharg_append(a, args[i]);
1265 sharg_append(a, uzbl.state.config_file);
1266 sharg_append(a, pid);
1267 sharg_append(a, xwin);
1268 sharg_append(a, uzbl.comm.fifo_path);
1269 sharg_append(a, uzbl.comm.socket_path);
1270 sharg_append(a, uzbl.state.uri);
1271 sharg_append(a, uzbl.gui.main_title);
1273 for (i = npre; i < g_strv_length((gchar**)args); i++)
1274 sharg_append(a, args[i]);
1278 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1280 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1281 NULL, NULL, output_stdout, NULL, NULL, &err);
1282 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1283 NULL, NULL, NULL, &err);
1285 if (uzbl.state.verbose) {
1286 GString *s = g_string_new("spawned:");
1287 for (i = 0; i < (a->len); i++) {
1288 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1289 g_string_append_printf(s, " %s", qarg);
1292 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1293 printf("%s\n", s->str);
1294 g_string_free(s, TRUE);
1296 printf("Stdout: %s\n", *output_stdout);
1300 g_printerr("error on run_command: %s\n", err->message);
1305 g_array_free (a, TRUE);
1310 split_quoted(const gchar* src, const gboolean unquote) {
1311 /* split on unquoted space, return array of strings;
1312 remove a layer of quotes and backslashes if unquote */
1313 if (!src) return NULL;
1315 gboolean dq = FALSE;
1316 gboolean sq = FALSE;
1317 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1318 GString *s = g_string_new ("");
1322 for (p = src; *p != '\0'; p++) {
1323 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1324 else if (*p == '\\') { g_string_append_c(s, *p++);
1325 g_string_append_c(s, *p); }
1326 else if ((*p == '"') && unquote && !sq) dq = !dq;
1327 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1329 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1330 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1332 else if ((*p == ' ') && !dq && !sq) {
1333 dup = g_strdup(s->str);
1334 g_array_append_val(a, dup);
1335 g_string_truncate(s, 0);
1336 } else g_string_append_c(s, *p);
1338 dup = g_strdup(s->str);
1339 g_array_append_val(a, dup);
1340 ret = (gchar**)a->data;
1341 g_array_free (a, FALSE);
1342 g_string_free (s, TRUE);
1347 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1348 (void)web_view; (void)result;
1349 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1350 if (argv_idx(argv, 0))
1351 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1355 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1356 (void)web_view; (void)result;
1358 if (argv_idx(argv, 0))
1359 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1360 TRUE, &uzbl.comm.sync_stdout);
1364 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1365 (void)web_view; (void)result;
1366 if (!uzbl.behave.shell_cmd) {
1367 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1372 gchar *spacer = g_strdup("");
1373 g_array_insert_val(argv, 1, spacer);
1374 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1376 for (i = 1; i < g_strv_length(cmd); i++)
1377 g_array_prepend_val(argv, cmd[i]);
1379 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1385 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1386 (void)web_view; (void)result;
1387 if (!uzbl.behave.shell_cmd) {
1388 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1393 gchar *spacer = g_strdup("");
1394 g_array_insert_val(argv, 1, spacer);
1395 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1397 for (i = 1; i < g_strv_length(cmd); i++)
1398 g_array_prepend_val(argv, cmd[i]);
1400 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1401 TRUE, &uzbl.comm.sync_stdout);
1407 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1408 (void)web_view; (void)result;
1411 struct sockaddr_un sa;
1418 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1420 /* This function could be optimised by storing a hash table of socket paths
1421 and associated connected file descriptors rather than closing and
1422 re-opening for every call. Also we could launch a script if socket connect
1425 /* First element argv[0] is path to socket. Following elements are tokens to
1426 write to the socket. We write them as a single packet with each token
1427 separated by an ASCII nul (\0). */
1429 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1434 /* copy socket path, null terminate result */
1435 sockpath = g_array_index(argv, char*, 0);
1436 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1437 sa.sun_family = AF_UNIX;
1439 /* create socket file descriptor and connect it to path */
1440 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1442 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1445 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1446 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1451 /* build request vector */
1452 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1454 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1458 for(i = 1; i < argv->len; ++i) {
1459 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1460 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1464 ret = writev(fd, iov, argv->len - 1);
1467 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1472 /* wait for a response, with a 500ms timeout */
1474 pfd.events = POLLIN;
1476 ret = poll(&pfd, 1, 500);
1478 if(ret == 0) errno = ETIMEDOUT;
1479 if(errno == EINTR) continue;
1480 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1486 /* get length of response */
1487 if(ioctl(fd, FIONREAD, &len) == -1) {
1488 g_printerr("talk_to_socket: cannot find daemon response length, "
1489 "ioctl failed (%s)\n", strerror(errno));
1494 /* if there is a response, read it */
1496 uzbl.comm.sync_stdout = g_malloc(len + 1);
1497 if(!uzbl.comm.sync_stdout) {
1498 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1502 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1504 ret = read(fd, uzbl.comm.sync_stdout, len);
1506 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1519 parse_command(const char *cmd, const char *param, GString *result) {
1522 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1524 gchar **par = split_quoted(param, TRUE);
1525 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1527 if (c->no_split) { /* don't split */
1528 sharg_append(a, param);
1530 for (i = 0; i < g_strv_length(par); i++)
1531 sharg_append(a, par[i]);
1534 if (result == NULL) {
1535 GString *result_print = g_string_new("");
1537 c->function(uzbl.gui.web_view, a, result_print);
1538 if (result_print->len)
1539 printf("%*s\n", result_print->len, result_print->str);
1541 g_string_free(result_print, TRUE);
1543 c->function(uzbl.gui.web_view, a, result);
1546 g_array_free (a, TRUE);
1549 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1556 if(*uzbl.net.proxy_url == ' '
1557 || uzbl.net.proxy_url == NULL) {
1558 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1559 (GType) SOUP_SESSION_PROXY_URI);
1562 suri = soup_uri_new(uzbl.net.proxy_url);
1563 g_object_set(G_OBJECT(uzbl.net.soup_session),
1564 SOUP_SESSION_PROXY_URI,
1566 soup_uri_free(suri);
1573 if(file_exists(uzbl.gui.icon)) {
1574 if (uzbl.gui.main_window)
1575 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1577 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1583 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1584 g_array_append_val (a, uzbl.state.uri);
1585 load_uri(uzbl.gui.web_view, a, NULL);
1586 g_array_free (a, TRUE);
1590 cmd_always_insert_mode() {
1591 set_insert_mode(uzbl.behave.always_insert_mode);
1597 g_object_set(G_OBJECT(uzbl.net.soup_session),
1598 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1602 cmd_max_conns_host() {
1603 g_object_set(G_OBJECT(uzbl.net.soup_session),
1604 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1609 soup_session_remove_feature
1610 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1611 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1612 /*g_free(uzbl.net.soup_logger);*/
1614 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1615 soup_session_add_feature(uzbl.net.soup_session,
1616 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1621 return webkit_web_view_get_settings(uzbl.gui.web_view);
1626 WebKitWebSettings *ws = view_settings();
1627 if (uzbl.behave.font_size > 0) {
1628 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1631 if (uzbl.behave.monospace_size > 0) {
1632 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1633 uzbl.behave.monospace_size, NULL);
1635 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1636 uzbl.behave.font_size, NULL);
1641 cmd_default_font_family() {
1642 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1643 uzbl.behave.default_font_family, NULL);
1647 cmd_monospace_font_family() {
1648 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1649 uzbl.behave.monospace_font_family, NULL);
1653 cmd_sans_serif_font_family() {
1654 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1655 uzbl.behave.sans_serif_font_family, NULL);
1659 cmd_serif_font_family() {
1660 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1661 uzbl.behave.serif_font_family, NULL);
1665 cmd_cursive_font_family() {
1666 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1667 uzbl.behave.cursive_font_family, NULL);
1671 cmd_fantasy_font_family() {
1672 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1673 uzbl.behave.fantasy_font_family, NULL);
1678 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1682 cmd_disable_plugins() {
1683 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1684 !uzbl.behave.disable_plugins, NULL);
1688 cmd_disable_scripts() {
1689 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1690 !uzbl.behave.disable_scripts, NULL);
1694 cmd_minimum_font_size() {
1695 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1696 uzbl.behave.minimum_font_size, NULL);
1699 cmd_autoload_img() {
1700 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1701 uzbl.behave.autoload_img, NULL);
1706 cmd_autoshrink_img() {
1707 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1708 uzbl.behave.autoshrink_img, NULL);
1713 cmd_enable_spellcheck() {
1714 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1715 uzbl.behave.enable_spellcheck, NULL);
1719 cmd_enable_private() {
1720 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1721 uzbl.behave.enable_private, NULL);
1726 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1727 uzbl.behave.print_bg, NULL);
1732 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1733 uzbl.behave.style_uri, NULL);
1737 cmd_resizable_txt() {
1738 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1739 uzbl.behave.resizable_txt, NULL);
1743 cmd_default_encoding() {
1744 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1745 uzbl.behave.default_encoding, NULL);
1749 cmd_enforce_96dpi() {
1750 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1751 uzbl.behave.enforce_96dpi, NULL);
1755 cmd_caret_browsing() {
1756 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1757 uzbl.behave.caret_browsing, NULL);
1761 cmd_cookie_handler() {
1762 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1763 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1764 if ((g_strcmp0(split[0], "sh") == 0) ||
1765 (g_strcmp0(split[0], "spawn") == 0)) {
1766 g_free (uzbl.behave.cookie_handler);
1767 uzbl.behave.cookie_handler =
1768 g_strdup_printf("sync_%s %s", split[0], split[1]);
1775 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1776 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1777 if ((g_strcmp0(split[0], "sh") == 0) ||
1778 (g_strcmp0(split[0], "spawn") == 0)) {
1779 g_free (uzbl.behave.new_window);
1780 uzbl.behave.new_window =
1781 g_strdup_printf("%s %s", split[0], split[1]);
1788 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1793 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1798 if(uzbl.behave.inject_html) {
1799 webkit_web_view_load_html_string (uzbl.gui.web_view,
1800 uzbl.behave.inject_html, NULL);
1809 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1810 uzbl.behave.modmask = 0;
1812 if(uzbl.behave.modkey)
1813 g_free(uzbl.behave.modkey);
1814 uzbl.behave.modkey = buf;
1816 for (i = 0; modkeys[i].key != NULL; i++) {
1817 if (g_strrstr(buf, modkeys[i].key))
1818 uzbl.behave.modmask |= modkeys[i].mask;
1824 if (*uzbl.net.useragent == ' ') {
1825 g_free (uzbl.net.useragent);
1826 uzbl.net.useragent = NULL;
1828 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1829 uzbl.net.useragent, NULL);
1835 gtk_widget_ref(uzbl.gui.scrolled_win);
1836 gtk_widget_ref(uzbl.gui.mainbar);
1837 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1838 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1840 if(uzbl.behave.status_top) {
1841 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1842 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1845 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1846 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1848 gtk_widget_unref(uzbl.gui.scrolled_win);
1849 gtk_widget_unref(uzbl.gui.mainbar);
1850 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1855 set_var_value(gchar *name, gchar *val) {
1856 uzbl_cmdprop *c = NULL;
1860 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1861 if(!c->writeable) return FALSE;
1863 /* check for the variable type */
1864 if (c->type == TYPE_STR) {
1865 buf = expand(val, 0);
1868 } else if(c->type == TYPE_INT) {
1869 int *ip = (int *)c->ptr;
1870 buf = expand(val, 0);
1871 *ip = (int)strtoul(buf, &endp, 10);
1873 } else if (c->type == TYPE_FLOAT) {
1874 float *fp = (float *)c->ptr;
1875 buf = expand(val, 0);
1876 *fp = strtod(buf, &endp);
1880 /* invoke a command specific function */
1881 if(c->func) c->func();
1888 Behaviour *b = &uzbl.behave;
1890 if(b->html_buffer->str) {
1891 webkit_web_view_load_html_string (uzbl.gui.web_view,
1892 b->html_buffer->str, b->base_url);
1893 g_string_free(b->html_buffer, TRUE);
1894 b->html_buffer = g_string_new("");
1898 enum {M_CMD, M_HTML};
1900 parse_cmd_line(const char *ctl_line, GString *result) {
1901 Behaviour *b = &uzbl.behave;
1904 if(b->mode == M_HTML) {
1905 len = strlen(b->html_endmarker);
1906 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1907 if(len == strlen(ctl_line)-1 &&
1908 !strncmp(b->html_endmarker, ctl_line, len)) {
1910 set_var_value("mode", "0");
1915 set_timeout(b->html_timeout);
1916 g_string_append(b->html_buffer, ctl_line);
1919 else if((ctl_line[0] == '#') /* Comments */
1920 || (ctl_line[0] == ' ')
1921 || (ctl_line[0] == '\n'))
1922 ; /* ignore these lines */
1923 else { /* parse a command */
1925 gchar **tokens = NULL;
1926 len = strlen(ctl_line);
1928 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1929 ctlstrip = g_strndup(ctl_line, len - 1);
1930 else ctlstrip = g_strdup(ctl_line);
1932 tokens = g_strsplit(ctlstrip, " ", 2);
1933 parse_command(tokens[0], tokens[1], result);
1940 build_stream_name(int type, const gchar* dir) {
1941 State *s = &uzbl.state;
1945 str = g_strdup_printf
1946 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1947 } else if (type == SOCKET) {
1948 str = g_strdup_printf
1949 ("%s/uzbl_socket_%s", dir, s->instance_name);
1955 control_fifo(GIOChannel *gio, GIOCondition condition) {
1956 if (uzbl.state.verbose)
1957 printf("triggered\n");
1962 if (condition & G_IO_HUP)
1963 g_error ("Fifo: Read end of pipe died!\n");
1966 g_error ("Fifo: GIOChannel broke\n");
1968 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1969 if (ret == G_IO_STATUS_ERROR) {
1970 g_error ("Fifo: Error reading: %s\n", err->message);
1974 parse_cmd_line(ctl_line, NULL);
1981 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1982 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1983 if (unlink(uzbl.comm.fifo_path) == -1)
1984 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1985 g_free(uzbl.comm.fifo_path);
1986 uzbl.comm.fifo_path = NULL;
1989 GIOChannel *chan = NULL;
1990 GError *error = NULL;
1991 gchar *path = build_stream_name(FIFO, dir);
1993 if (!file_exists(path)) {
1994 if (mkfifo (path, 0666) == 0) {
1995 // we don't really need to write to the file, but if we open the file as 'r' we will block here, waiting for a writer to open the file.
1996 chan = g_io_channel_new_file(path, "r+", &error);
1998 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1999 if (uzbl.state.verbose)
2000 printf ("init_fifo: created successfully as %s\n", path);
2001 uzbl.comm.fifo_path = path;
2003 } else g_warning ("init_fifo: could not add watch on %s\n", path);
2004 } else g_warning ("init_fifo: can't open: %s\n", error->message);
2005 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
2006 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
2008 /* if we got this far, there was an error; cleanup */
2009 if (error) g_error_free (error);
2016 control_stdin(GIOChannel *gio, GIOCondition condition) {
2018 gchar *ctl_line = NULL;
2021 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2022 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2025 parse_cmd_line(ctl_line, NULL);
2033 GIOChannel *chan = NULL;
2034 GError *error = NULL;
2036 chan = g_io_channel_unix_new(fileno(stdin));
2038 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2039 g_error ("Stdin: could not add watch\n");
2041 if (uzbl.state.verbose)
2042 printf ("Stdin: watch added successfully\n");
2045 g_error ("Stdin: Error while opening: %s\n", error->message);
2047 if (error) g_error_free (error);
2051 control_socket(GIOChannel *chan) {
2052 struct sockaddr_un remote;
2053 unsigned int t = sizeof(remote);
2055 GIOChannel *clientchan;
2057 clientsock = accept (g_io_channel_unix_get_fd(chan),
2058 (struct sockaddr *) &remote, &t);
2060 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2061 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2062 (GIOFunc) control_client_socket, clientchan);
2069 control_client_socket(GIOChannel *clientchan) {
2071 GString *result = g_string_new("");
2072 GError *error = NULL;
2076 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2077 if (ret == G_IO_STATUS_ERROR) {
2078 g_warning ("Error reading: %s\n", error->message);
2079 g_io_channel_shutdown(clientchan, TRUE, &error);
2081 } else if (ret == G_IO_STATUS_EOF) {
2082 /* shutdown and remove channel watch from main loop */
2083 g_io_channel_shutdown(clientchan, TRUE, &error);
2088 parse_cmd_line (ctl_line, result);
2089 g_string_append_c(result, '\n');
2090 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2092 if (ret == G_IO_STATUS_ERROR) {
2093 g_warning ("Error writing: %s", error->message);
2095 g_io_channel_flush(clientchan, &error);
2098 if (error) g_error_free (error);
2099 g_string_free(result, TRUE);
2105 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2106 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2107 if (unlink(uzbl.comm.socket_path) == -1)
2108 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2109 g_free(uzbl.comm.socket_path);
2110 uzbl.comm.socket_path = NULL;
2118 GIOChannel *chan = NULL;
2120 struct sockaddr_un local;
2121 gchar *path = build_stream_name(SOCKET, dir);
2123 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2125 local.sun_family = AF_UNIX;
2126 strcpy (local.sun_path, path);
2127 unlink (local.sun_path);
2129 len = strlen (local.sun_path) + sizeof (local.sun_family);
2130 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2131 if (uzbl.state.verbose)
2132 printf ("init_socket: opened in %s\n", path);
2135 if( (chan = g_io_channel_unix_new(sock)) ) {
2136 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2137 uzbl.comm.socket_path = path;
2140 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2142 /* if we got this far, there was an error; cleanup */
2149 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2150 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2152 // this function may be called very early when the templates are not set (yet), hence the checks
2154 update_title (void) {
2155 Behaviour *b = &uzbl.behave;
2158 if (b->show_status) {
2159 if (b->title_format_short) {
2160 parsed = expand(b->title_format_short, 0);
2161 if (uzbl.gui.main_window)
2162 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2165 if (b->status_format) {
2166 parsed = expand(b->status_format, 0);
2167 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2170 if (b->status_background) {
2172 gdk_color_parse (b->status_background, &color);
2173 //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2174 if (uzbl.gui.main_window)
2175 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2176 else if (uzbl.gui.plug)
2177 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2180 if (b->title_format_long) {
2181 parsed = expand(b->title_format_long, 0);
2182 if (uzbl.gui.main_window)
2183 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2190 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2194 retrieve_geometry();
2199 key_press_cb (GtkWidget* window, GdkEventKey* event)
2201 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2205 if (event->type != GDK_KEY_PRESS ||
2206 event->keyval == GDK_Page_Up ||
2207 event->keyval == GDK_Page_Down ||
2208 event->keyval == GDK_Up ||
2209 event->keyval == GDK_Down ||
2210 event->keyval == GDK_Left ||
2211 event->keyval == GDK_Right ||
2212 event->keyval == GDK_Shift_L ||
2213 event->keyval == GDK_Shift_R)
2216 /* turn off insert mode (if always_insert_mode is not used) */
2217 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2218 set_insert_mode(uzbl.behave.always_insert_mode);
2223 if (uzbl.behave.insert_mode &&
2224 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2225 (!uzbl.behave.modmask)
2230 if (event->keyval == GDK_Escape) {
2233 dehilight(uzbl.gui.web_view, NULL, NULL);
2237 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2238 if (event->keyval == GDK_Insert) {
2240 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2241 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2243 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2246 GString* keycmd = g_string_new(uzbl.state.keycmd);
2247 g_string_append (keycmd, str);
2248 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2255 if (event->keyval == GDK_BackSpace)
2256 keycmd_bs(NULL, NULL, NULL);
2258 gboolean key_ret = FALSE;
2259 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2262 GString* keycmd = g_string_new(uzbl.state.keycmd);
2263 g_string_append(keycmd, event->string);
2264 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2267 run_keycmd(key_ret);
2269 if (key_ret) return (!uzbl.behave.insert_mode);
2274 run_keycmd(const gboolean key_ret) {
2275 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2277 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2279 parse_command(act->name, act->param, NULL);
2283 /* try if it's an incremental keycmd or one that takes args, and run it */
2284 GString* short_keys = g_string_new ("");
2285 GString* short_keys_inc = g_string_new ("");
2287 guint len = strlen(uzbl.state.keycmd);
2288 for (i=0; i<len; i++) {
2289 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2290 g_string_assign(short_keys_inc, short_keys->str);
2291 g_string_append_c(short_keys, '_');
2292 g_string_append_c(short_keys_inc, '*');
2294 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2295 /* run normal cmds only if return was pressed */
2296 exec_paramcmd(act, i);
2299 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2300 if (key_ret) /* just quit the incremental command on return */
2302 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2306 g_string_truncate(short_keys, short_keys->len - 1);
2308 g_string_free (short_keys, TRUE);
2309 g_string_free (short_keys_inc, TRUE);
2313 exec_paramcmd(const Action *act, const guint i) {
2314 GString *parampart = g_string_new (uzbl.state.keycmd);
2315 GString *actionname = g_string_new ("");
2316 GString *actionparam = g_string_new ("");
2317 g_string_erase (parampart, 0, i+1);
2319 g_string_printf (actionname, act->name, parampart->str);
2321 g_string_printf (actionparam, act->param, parampart->str);
2322 parse_command(actionname->str, actionparam->str, NULL);
2323 g_string_free(actionname, TRUE);
2324 g_string_free(actionparam, TRUE);
2325 g_string_free(parampart, TRUE);
2333 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2335 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2336 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2337 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2338 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2339 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2340 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2341 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2342 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2343 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2344 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2345 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2352 g->mainbar = gtk_hbox_new (FALSE, 0);
2354 /* keep a reference to the bar so we can re-pack it at runtime*/
2355 //sbar_ref = g_object_ref(g->mainbar);
2357 g->mainbar_label = gtk_label_new ("");
2358 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2359 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2360 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2361 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2362 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2363 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2369 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2370 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2371 gtk_widget_set_name (window, "Uzbl browser");
2372 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2373 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2374 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2381 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2382 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2383 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2390 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2392 If actname is one that calls an external command, this function will inject
2393 newargs in front of the user-provided args in that command line. They will
2394 come become after the body of the script (in sh) or after the name of
2395 the command to execute (in spawn).
2396 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2397 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2399 The return value consist of two strings: the action (sh, ...) and its args.
2401 If act is not one that calls an external command, then the given action merely
2404 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2405 /* Arrr! Here be memory leaks */
2406 gchar *actdup = g_strdup(actname);
2407 g_array_append_val(rets, actdup);
2409 if ((g_strcmp0(actname, "spawn") == 0) ||
2410 (g_strcmp0(actname, "sh") == 0) ||
2411 (g_strcmp0(actname, "sync_spawn") == 0) ||
2412 (g_strcmp0(actname, "sync_sh") == 0) ||
2413 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2415 GString *a = g_string_new("");
2416 gchar **spawnparts = split_quoted(origargs, FALSE);
2417 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2418 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2420 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2421 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2423 g_array_append_val(rets, a->str);
2424 g_string_free(a, FALSE);
2425 g_strfreev(spawnparts);
2427 gchar *origdup = g_strdup(origargs);
2428 g_array_append_val(rets, origdup);
2430 return (gchar**)g_array_free(rets, FALSE);
2434 run_handler (const gchar *act, const gchar *args) {
2435 /* Consider this code a temporary hack to make the handlers usable.
2436 In practice, all this splicing, injection, and reconstruction is
2437 inefficient, annoying and hard to manage. Potential pitfalls arise
2438 when the handler specific args 1) are not quoted (the handler
2439 callbacks should take care of this) 2) are quoted but interfere
2440 with the users' own quotation. A more ideal solution is
2441 to refactor parse_command so that it doesn't just take a string
2442 and execute it; rather than that, we should have a function which
2443 returns the argument vector parsed from the string. This vector
2444 could be modified (e.g. insert additional args into it) before
2445 passing it to the next function that actually executes it. Though
2446 it still isn't perfect for chain actions.. will reconsider & re-
2447 factor when I have the time. -duc */
2449 char **parts = g_strsplit(act, " ", 2);
2451 if (g_strcmp0(parts[0], "chain") == 0) {
2452 GString *newargs = g_string_new("");
2453 gchar **chainparts = split_quoted(parts[1], FALSE);
2455 /* for every argument in the chain, inject the handler args
2456 and make sure the new parts are wrapped in quotes */
2457 gchar **cp = chainparts;
2459 gchar *quotless = NULL;
2460 gchar **spliced_quotless = NULL; // sigh -_-;
2461 gchar **inpart = NULL;
2464 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2466 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2467 } else quotless = g_strdup(*cp);
2469 spliced_quotless = g_strsplit(quotless, " ", 2);
2470 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2471 g_strfreev(spliced_quotless);
2473 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2479 parse_command(parts[0], &(newargs->str[1]), NULL);
2480 g_string_free(newargs, TRUE);
2481 g_strfreev(chainparts);
2484 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2485 parse_command(inparts[0], inparts[1], NULL);
2493 add_binding (const gchar *key, const gchar *act) {
2494 char **parts = g_strsplit(act, " ", 2);
2501 if (uzbl.state.verbose)
2502 printf ("Binding %-10s : %s\n", key, act);
2503 action = new_action(parts[0], parts[1]);
2505 if (g_hash_table_remove (uzbl.bindings, key))
2506 g_warning ("Overwriting existing binding for \"%s\"", key);
2507 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2512 get_xdg_var (XDG_Var xdg) {
2513 const gchar* actual_value = getenv (xdg.environmental);
2514 const gchar* home = getenv ("HOME");
2515 gchar* return_value;
2517 if (! actual_value || strcmp (actual_value, "") == 0) {
2518 if (xdg.default_value) {
2519 return_value = str_replace ("~", home, xdg.default_value);
2521 return_value = NULL;
2524 return_value = str_replace("~", home, actual_value);
2527 return return_value;
2531 find_xdg_file (int xdg_type, char* filename) {
2532 /* xdg_type = 0 => config
2533 xdg_type = 1 => data
2534 xdg_type = 2 => cache*/
2536 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2537 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2540 gchar* temporary_string;
2544 if (! file_exists (temporary_file) && xdg_type != 2) {
2545 buf = get_xdg_var (XDG[3 + xdg_type]);
2546 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2549 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2550 g_free (temporary_file);
2551 temporary_file = g_strconcat (temporary_string, filename, NULL);
2555 //g_free (temporary_string); - segfaults.
2557 if (file_exists (temporary_file)) {
2558 return temporary_file;
2565 State *s = &uzbl.state;
2566 Network *n = &uzbl.net;
2568 for (i = 0; default_config[i].command != NULL; i++) {
2569 parse_cmd_line(default_config[i].command, NULL);
2572 if (g_strcmp0(s->config_file, "-") == 0) {
2573 s->config_file = NULL;
2577 else if (!s->config_file) {
2578 s->config_file = find_xdg_file (0, "/uzbl/config");
2581 if (s->config_file) {
2582 GArray* lines = read_file_by_line (s->config_file);
2586 while ((line = g_array_index(lines, gchar*, i))) {
2587 parse_cmd_line (line, NULL);
2591 g_array_free (lines, TRUE);
2593 if (uzbl.state.verbose)
2594 printf ("No configuration file loaded.\n");
2597 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2600 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2603 if (!uzbl.behave.cookie_handler)
2606 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2607 GString *s = g_string_new ("");
2608 SoupURI * soup_uri = soup_message_get_uri(msg);
2609 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2610 run_handler(uzbl.behave.cookie_handler, s->str);
2612 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2613 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2614 if ( p != NULL ) *p = '\0';
2615 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2617 if (uzbl.comm.sync_stdout)
2618 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2620 g_string_free(s, TRUE);
2624 save_cookies (SoupMessage *msg, gpointer user_data){
2628 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2629 cookie = soup_cookie_to_set_cookie_header(ck->data);
2630 SoupURI * soup_uri = soup_message_get_uri(msg);
2631 GString *s = g_string_new ("");
2632 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2633 run_handler(uzbl.behave.cookie_handler, s->str);
2635 g_string_free(s, TRUE);
2640 /* --- WEBINSPECTOR --- */
2642 hide_window_cb(GtkWidget *widget, gpointer data) {
2645 gtk_widget_hide(widget);
2649 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2652 (void) web_inspector;
2653 GtkWidget* scrolled_window;
2654 GtkWidget* new_web_view;
2657 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2658 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2659 G_CALLBACK(hide_window_cb), NULL);
2661 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2662 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2663 gtk_widget_show(g->inspector_window);
2665 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2666 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2667 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2668 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2669 gtk_widget_show(scrolled_window);
2671 new_web_view = webkit_web_view_new();
2672 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2674 return WEBKIT_WEB_VIEW(new_web_view);
2678 inspector_show_window_cb (WebKitWebInspector* inspector){
2680 gtk_widget_show(uzbl.gui.inspector_window);
2684 /* TODO: Add variables and code to make use of these functions */
2686 inspector_close_window_cb (WebKitWebInspector* inspector){
2692 inspector_attach_window_cb (WebKitWebInspector* inspector){
2698 inspector_detach_window_cb (WebKitWebInspector* inspector){
2704 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2710 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2716 set_up_inspector() {
2718 WebKitWebSettings *settings = view_settings();
2719 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2721 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2722 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2723 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2724 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2725 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2726 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2727 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2729 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2733 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2735 uzbl_cmdprop *c = v;
2740 if(c->type == TYPE_STR)
2741 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2742 else if(c->type == TYPE_INT)
2743 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2744 else if(c->type == TYPE_FLOAT)
2745 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2749 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2753 printf("bind %s = %s %s\n", (char *)k ,
2754 (char *)a->name, a->param?(char *)a->param:"");
2759 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2760 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2764 retrieve_geometry() {
2766 GString *buf = g_string_new("");
2768 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2769 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2771 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2773 if(uzbl.gui.geometry)
2774 g_free(uzbl.gui.geometry);
2775 uzbl.gui.geometry = g_string_free(buf, FALSE);
2778 /* set up gtk, gobject, variable defaults and other things that tests and other
2779 * external applications need to do anyhow */
2781 initialize(int argc, char *argv[]) {
2782 if (!g_thread_supported ())
2783 g_thread_init (NULL);
2784 uzbl.state.executable_path = g_strdup(argv[0]);
2785 uzbl.state.selected_url = NULL;
2786 uzbl.state.searchtx = NULL;
2788 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2789 g_option_context_add_main_entries (context, entries, NULL);
2790 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2791 g_option_context_parse (context, &argc, &argv, NULL);
2792 g_option_context_free(context);
2794 if (uzbl.behave.print_version) {
2795 printf("Commit: %s\n", COMMIT);
2799 /* initialize hash table */
2800 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2802 uzbl.net.soup_session = webkit_get_default_session();
2803 uzbl.state.keycmd = g_strdup("");
2805 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2806 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2807 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2808 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2809 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2810 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2812 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2813 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2814 uzbl.gui.sbar.progress_w = 10;
2816 /* HTML mode defaults*/
2817 uzbl.behave.html_buffer = g_string_new("");
2818 uzbl.behave.html_endmarker = g_strdup(".");
2819 uzbl.behave.html_timeout = 60;
2820 uzbl.behave.base_url = g_strdup("http://invalid");
2822 /* default mode indicators */
2823 uzbl.behave.insert_indicator = g_strdup("I");
2824 uzbl.behave.cmd_indicator = g_strdup("C");
2826 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2827 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2828 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2829 uzbl.info.arch = ARCH;
2830 uzbl.info.commit = COMMIT;
2833 make_var_to_name_hash();
2838 #ifndef UZBL_LIBRARY
2841 main (int argc, char* argv[]) {
2842 initialize(argc, argv);
2844 gtk_init (&argc, &argv);
2846 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2847 //main_window_ref = g_object_ref(scrolled_window);
2848 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2849 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2851 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2852 GTK_WIDGET (uzbl.gui.web_view));
2854 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2858 /* initial packing */
2859 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2860 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2862 if (uzbl.state.socket_id) {
2863 uzbl.gui.plug = create_plug ();
2864 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2865 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2867 uzbl.gui.main_window = create_window ();
2868 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2869 gtk_widget_show_all (uzbl.gui.main_window);
2870 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2873 if(!uzbl.state.instance_name)
2874 uzbl.state.instance_name = itos((int)uzbl.xwin);
2876 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2878 if (uzbl.state.verbose) {
2879 printf("Uzbl start location: %s\n", argv[0]);
2880 if (uzbl.state.socket_id)
2881 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2883 printf("window_id %i\n",(int) uzbl.xwin);
2884 printf("pid %i\n", getpid ());
2885 printf("name: %s\n", uzbl.state.instance_name);
2888 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2889 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2890 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2891 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2892 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2894 /* Check uzbl is in window mode before getting/setting geometry */
2895 if (uzbl.gui.main_window) {
2896 if(uzbl.gui.geometry)
2899 retrieve_geometry();
2902 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2903 if (argc > 1 && !uzbl.state.uri)
2904 uri_override = g_strdup(argv[1]);
2905 gboolean verbose_override = uzbl.state.verbose;
2908 set_insert_mode(FALSE);
2910 if (!uzbl.behave.show_status)
2911 gtk_widget_hide(uzbl.gui.mainbar);
2918 if (verbose_override > uzbl.state.verbose)
2919 uzbl.state.verbose = verbose_override;
2922 set_var_value("uri", uri_override);
2923 g_free(uri_override);
2924 } else if (uzbl.state.uri)
2925 cmd_load_uri(uzbl.gui.web_view, NULL);
2930 return EXIT_SUCCESS;
2934 /* vi: set et ts=4: */