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])
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
45 #include <webkit/webkit.h>
46 #include <libsoup/soup.h>
47 #include <JavaScriptCore/JavaScript.h>
59 #include <sys/ioctl.h>
66 /* commandline arguments (set initial values for the state variables) */
68 GOptionEntry entries[] =
70 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
71 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
72 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
73 "Whether to print all messages or just errors.", NULL },
74 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
75 "Name of the current instance (defaults to Xorg window id)", "NAME" },
76 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
77 "Path to config file or '-' for stdin", "FILE" },
78 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
79 "Socket ID", "SOCKET" },
80 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
81 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
82 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
83 "Print the version and exit", NULL },
84 { NULL, 0, 0, 0, NULL, NULL, NULL }
87 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
89 /* associate command names to their properties */
99 /*@null@*/ void (*func)(void);
102 /* abbreviations to help keep the table's width humane */
103 #define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun }
104 #define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun }
105 #define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun }
106 #define PTR_C_STR(var, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun }
107 #define PTR_C_INT(var, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun }
108 #define PTR_C_FLOAT(var, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun }
110 const struct var_name_to_ptr_t {
113 } var_name_to_ptr[] = {
114 /* variable name pointer to variable in code dump callback function */
115 /* ---------------------------------------------------------------------------------------------- */
116 { "uri", PTR_V_STR(uzbl.state.uri, 1, cmd_load_uri)},
117 { "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)},
118 { "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)},
119 { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
120 { "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
121 { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
122 { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
123 { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
124 { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
125 { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
126 { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
127 { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
128 { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
129 { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
130 { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
131 { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
132 { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
133 { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
134 { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
135 { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
136 { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
137 { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
138 { "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
139 { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
140 { "history_handler", PTR_V_STR(uzbl.behave.history_handler, 1, NULL)},
141 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
142 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
143 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
144 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
145 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
146 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
147 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
148 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
149 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
150 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
151 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
153 /* exported WebKitWebSettings properties */
154 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
155 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
156 { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)},
157 { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)},
158 { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)},
159 { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)},
160 { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)},
161 { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)},
162 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
163 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
164 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
165 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
166 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
167 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
168 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
169 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
170 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
171 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
172 { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
173 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
174 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
175 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
177 /* constants (not dumpable or writeable) */
178 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
179 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
180 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
181 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
182 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
183 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
184 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
185 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
186 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
187 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
188 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
190 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
195 /*@null@*/ char *key;
198 { "SHIFT", GDK_SHIFT_MASK }, // shift
199 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
200 { "CONTROL", GDK_CONTROL_MASK }, // control
201 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
202 { "MOD2", GDK_MOD2_MASK }, // 5th mod
203 { "MOD3", GDK_MOD3_MASK }, // 6th mod
204 { "MOD4", GDK_MOD4_MASK }, // 7th mod
205 { "MOD5", GDK_MOD5_MASK }, // 8th mod
206 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
207 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
208 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
209 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
210 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
211 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
212 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
213 { "META", GDK_META_MASK }, // meta (since 2.10)
218 /* construct a hash from the var_name_to_ptr array for quick access */
220 make_var_to_name_hash() {
221 const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
222 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
224 g_hash_table_insert(uzbl.comm.proto_var,
225 (gpointer) n2v_p->name,
226 (gpointer) &n2v_p->cp);
231 /* --- UTILITY FUNCTIONS --- */
232 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
234 get_exp_type(const gchar *s) {
238 else if(*(s+1) == '{')
239 return EXP_BRACED_VAR;
240 else if(*(s+1) == '<')
242 else if(*(s+1) == '[')
245 return EXP_SIMPLE_VAR;
252 * recurse == 1: don't expand '@(command)@'
253 * recurse == 2: don't expand '@<java script>@'
256 expand(const char *s, guint recurse) {
259 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
263 gchar *cmd_stdout = NULL;
265 GString *buf = g_string_new("");
266 GString *js_ret = g_string_new("");
271 g_string_append_c(buf, *++s);
276 etype = get_exp_type(s);
281 vend = strpbrk(s, end_simple_var);
282 if(!vend) vend = strchr(s, '\0');
286 vend = strchr(s, '}');
287 if(!vend) vend = strchr(s, '\0');
291 vend = strstr(s, ")@");
292 if(!vend) vend = strchr(s, '\0');
296 vend = strstr(s, ">@");
297 if(!vend) vend = strchr(s, '\0');
301 vend = strstr(s, "]@");
302 if(!vend) vend = strchr(s, '\0');
310 ret = g_strndup(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.s != NULL) {
316 g_string_append(buf, (gchar *)*c->ptr.s);
318 else if(c->type == TYPE_INT) {
319 g_string_append_printf(buf, "%d", *c->ptr.i);
321 else if(c->type == TYPE_FLOAT) {
322 g_string_append_printf(buf, "%f", *c->ptr.f);
326 if(etype == EXP_SIMPLE_VAR)
331 else if(recurse != 1 &&
333 mycmd = expand(ret, 1);
334 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
338 g_printerr("error on running command: %s\n", err->message);
341 else if (*cmd_stdout) {
342 size_t len = strlen(cmd_stdout);
344 if(len > 0 && cmd_stdout[len-1] == '\n')
345 cmd_stdout[--len] = '\0'; /* strip trailing newline */
347 g_string_append(buf, cmd_stdout);
352 else if(recurse != 2 &&
354 mycmd = expand(ret, 2);
355 eval_js(uzbl.gui.web_view, mycmd, js_ret);
359 g_string_append(buf, js_ret->str);
360 g_string_free(js_ret, TRUE);
361 js_ret = g_string_new("");
365 else if(etype == EXP_ESCAPE) {
366 mycmd = expand(ret, 0);
367 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
369 g_string_append(buf, escaped);
381 g_string_append_c(buf, *s);
386 g_string_free(js_ret, TRUE);
387 return g_string_free(buf, FALSE);
394 snprintf(tmp, sizeof(tmp), "%i", val);
395 return g_strdup(tmp);
399 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
402 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
405 str_replace (const char* search, const char* replace, const char* string) {
409 buf = g_strsplit (string, search, -1);
410 ret = g_strjoinv (replace, buf);
411 g_strfreev(buf); // somebody said this segfaults
417 read_file_by_line (const gchar *path) {
418 GIOChannel *chan = NULL;
419 gchar *readbuf = NULL;
421 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
424 chan = g_io_channel_new_file(path, "r", NULL);
427 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
428 const gchar* val = g_strdup (readbuf);
429 g_array_append_val (lines, val);
434 g_io_channel_unref (chan);
436 fprintf(stderr, "File '%s' not be read.\n", path);
443 parseenv (char* string) {
444 extern char** environ;
445 gchar* tmpstr = NULL;
449 while (environ[i] != NULL) {
450 gchar** env = g_strsplit (environ[i], "=", 2);
451 gchar* envname = g_strconcat ("$", env[0], NULL);
453 if (g_strrstr (string, envname) != NULL) {
454 tmpstr = g_strdup(string);
456 string = str_replace(envname, env[1], tmpstr);
461 g_strfreev (env); // somebody said this breaks uzbl
469 setup_signal(int signr, sigfunc *shandler) {
470 struct sigaction nh, oh;
472 nh.sa_handler = shandler;
473 sigemptyset(&nh.sa_mask);
476 if(sigaction(signr, &nh, &oh) < 0)
484 if (uzbl.behave.fifo_dir)
485 unlink (uzbl.comm.fifo_path);
486 if (uzbl.behave.socket_dir)
487 unlink (uzbl.comm.socket_path);
489 g_free(uzbl.state.executable_path);
490 g_free(uzbl.state.keycmd);
491 g_hash_table_destroy(uzbl.bindings);
492 g_hash_table_destroy(uzbl.behave.commands);
495 /* --- SIGNAL HANDLER --- */
498 catch_sigterm(int s) {
504 catch_sigint(int s) {
510 /* --- CALLBACKS --- */
513 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
516 (void) navigation_action;
517 (void) policy_decision;
519 const gchar* uri = webkit_network_request_get_uri (request);
520 if (uzbl.state.verbose)
521 printf("New window requested -> %s \n", uri);
522 webkit_web_policy_decision_use(policy_decision);
527 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
532 /* If we can display it, let's display it... */
533 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
534 webkit_web_policy_decision_use (policy_decision);
538 /* ...everything we can't displayed is downloaded */
539 webkit_web_policy_decision_download (policy_decision);
543 /*@null@*/ WebKitWebView*
544 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
548 if (uzbl.state.selected_url != NULL) {
549 if (uzbl.state.verbose)
550 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
551 new_window_load_uri(uzbl.state.selected_url);
553 if (uzbl.state.verbose)
554 printf("New web view -> %s\n","Nothing to open, exiting");
560 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
563 if (uzbl.behave.download_handler) {
564 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
565 if (uzbl.state.verbose)
566 printf("Download -> %s\n",uri);
567 /* if urls not escaped, we may have to escape and quote uri before this call */
569 GString *args = g_string_new(uri);
571 if (uzbl.net.proxy_url) {
572 g_string_append_c(args, ' ');
573 g_string_append(args, uzbl.net.proxy_url);
576 run_handler(uzbl.behave.download_handler, args->str);
578 g_string_free(args, TRUE);
583 /* scroll a bar in a given direction */
585 scroll (GtkAdjustment* bar, GArray *argv) {
589 gdouble page_size = gtk_adjustment_get_page_size(bar);
590 gdouble value = gtk_adjustment_get_value(bar);
591 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
594 value += page_size * amount * 0.01;
598 max_value = gtk_adjustment_get_upper(bar) - page_size;
600 if (value > max_value)
601 value = max_value; /* don't scroll past the end of the page */
603 gtk_adjustment_set_value (bar, value);
607 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
608 (void) page; (void) argv; (void) result;
609 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
613 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
614 (void) page; (void) argv; (void) result;
615 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
616 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
620 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
621 (void) page; (void) result;
622 scroll(uzbl.gui.bar_v, argv);
626 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
627 (void) page; (void) result;
628 scroll(uzbl.gui.bar_h, argv);
633 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
634 if(uzbl.state.verbose)
635 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
637 /* update geometry var with the actual geometry
638 this is necessary as some WMs don't seem to honour
639 the above setting and we don't want to end up with
640 wrong geometry information
647 if (!uzbl.behave.show_status) {
648 gtk_widget_hide(uzbl.gui.mainbar);
650 gtk_widget_show(uzbl.gui.mainbar);
656 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
661 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
665 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
670 if (uzbl.behave.show_status) {
671 gtk_widget_hide(uzbl.gui.mainbar);
673 gtk_widget_show(uzbl.gui.mainbar);
675 uzbl.behave.show_status = !uzbl.behave.show_status;
680 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
684 //Set selected_url state variable
685 g_free(uzbl.state.selected_url);
686 uzbl.state.selected_url = NULL;
688 uzbl.state.selected_url = g_strdup(link);
694 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
697 const gchar *title = webkit_web_view_get_title(web_view);
698 if (uzbl.gui.main_title)
699 g_free (uzbl.gui.main_title);
700 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
705 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
708 uzbl.gui.sbar.load_progress = progress;
710 g_free(uzbl.gui.sbar.progress_bar);
711 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
717 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
721 if (uzbl.behave.load_finish_handler)
722 run_handler(uzbl.behave.load_finish_handler, "");
725 void clear_keycmd() {
726 g_free(uzbl.state.keycmd);
727 uzbl.state.keycmd = g_strdup("");
731 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
735 uzbl.gui.sbar.load_progress = 0;
736 clear_keycmd(); // don't need old commands to remain on new page?
737 if (uzbl.behave.load_start_handler)
738 run_handler(uzbl.behave.load_start_handler, "");
742 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
745 g_free (uzbl.state.uri);
746 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
747 uzbl.state.uri = g_string_free (newuri, FALSE);
748 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
749 set_insert_mode(uzbl.behave.always_insert_mode);
752 if (uzbl.behave.load_commit_handler)
753 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
757 destroy_cb (GtkWidget* widget, gpointer data) {
765 if (uzbl.behave.history_handler) {
767 struct tm * timeinfo;
770 timeinfo = localtime ( &rawtime );
771 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
772 run_handler(uzbl.behave.history_handler, date);
777 /* VIEW funcs (little webkit wrappers) */
778 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
780 VIEWFUNC(reload_bypass_cache)
781 VIEWFUNC(stop_loading)
788 /* -- command to callback/function map for things we cannot attach to any signals */
789 struct {const char *key; CommandInfo value;} cmdlist[] =
790 { /* key function no_split */
791 { "back", {view_go_back, 0} },
792 { "forward", {view_go_forward, 0} },
793 { "scroll_vert", {scroll_vert, 0} },
794 { "scroll_horz", {scroll_horz, 0} },
795 { "scroll_begin", {scroll_begin, 0} },
796 { "scroll_end", {scroll_end, 0} },
797 { "reload", {view_reload, 0}, },
798 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
799 { "stop", {view_stop_loading, 0}, },
800 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
801 { "zoom_out", {view_zoom_out, 0}, },
802 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
803 { "uri", {load_uri, TRUE} },
804 { "js", {run_js, TRUE} },
805 { "script", {run_external_js, 0} },
806 { "toggle_status", {toggle_status_cb, 0} },
807 { "spawn", {spawn, 0} },
808 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
809 { "sh", {spawn_sh, 0} },
810 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
811 { "talk_to_socket", {talk_to_socket, 0} },
812 { "exit", {close_uzbl, 0} },
813 { "search", {search_forward_text, TRUE} },
814 { "search_reverse", {search_reverse_text, TRUE} },
815 { "dehilight", {dehilight, 0} },
816 { "toggle_insert_mode", {toggle_insert_mode, 0} },
817 { "set", {set_var, TRUE} },
818 //{ "get", {get_var, TRUE} },
819 { "bind", {act_bind, TRUE} },
820 { "dump_config", {act_dump_config, 0} },
821 { "keycmd", {keycmd, TRUE} },
822 { "keycmd_nl", {keycmd_nl, TRUE} },
823 { "keycmd_bs", {keycmd_bs, 0} },
824 { "chain", {chain, 0} },
825 { "print", {print, TRUE} },
826 { "update_gui", {update_gui, TRUE} }
833 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
835 for (i = 0; i < LENGTH(cmdlist); i++)
836 g_hash_table_insert(uzbl.behave.commands, (gpointer) cmdlist[i].key, &cmdlist[i].value);
839 /* -- CORE FUNCTIONS -- */
842 free_action(gpointer act) {
843 Action *action = (Action*)act;
844 g_free(action->name);
846 g_free(action->param);
851 new_action(const gchar *name, const gchar *param) {
852 Action *action = g_new(Action, 1);
854 action->name = g_strdup(name);
856 action->param = g_strdup(param);
858 action->param = NULL;
864 file_exists (const char * filename) {
865 return (access(filename, F_OK) == 0);
869 set_var(WebKitWebView *page, GArray *argv, GString *result) {
870 (void) page; (void) result;
871 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
872 if (split[0] != NULL) {
873 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
874 set_var_value(g_strstrip(split[0]), value);
881 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
882 (void) page; (void) argv; (void) result;
888 print(WebKitWebView *page, GArray *argv, GString *result) {
889 (void) page; (void) result;
892 buf = expand(argv_idx(argv, 0), 0);
893 g_string_assign(result, buf);
898 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
899 (void) page; (void) result;
900 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
901 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
902 add_binding(g_strstrip(split[0]), value);
920 set_mode_indicator() {
921 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
922 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
927 set_mode_indicator();
932 set_insert_mode(gboolean mode) {
933 uzbl.behave.insert_mode = mode;
934 set_mode_indicator();
938 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
939 (void) page; (void) result;
941 if (argv_idx(argv, 0)) {
942 if (strcmp (argv_idx(argv, 0), "0") == 0) {
943 set_insert_mode(FALSE);
945 set_insert_mode(TRUE);
948 set_insert_mode( !uzbl.behave.insert_mode );
955 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
958 if (argv_idx(argv, 0)) {
959 GString* newuri = g_string_new (argv_idx(argv, 0));
960 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
961 run_js(web_view, argv, NULL);
964 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
965 g_string_prepend (newuri, "http://");
966 /* if we do handle cookies, ask our handler for them */
967 webkit_web_view_load_uri (web_view, newuri->str);
968 g_string_free (newuri, TRUE);
975 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
976 size_t argumentCount, const JSValueRef arguments[],
977 JSValueRef* exception) {
982 JSStringRef js_result_string;
983 GString *result = g_string_new("");
985 if (argumentCount >= 1) {
986 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
987 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
988 char ctl_line[arg_size];
989 JSStringGetUTF8CString(arg, ctl_line, arg_size);
991 parse_cmd_line(ctl_line, result);
993 JSStringRelease(arg);
995 js_result_string = JSStringCreateWithUTF8CString(result->str);
997 g_string_free(result, TRUE);
999 return JSValueMakeString(ctx, js_result_string);
1002 JSStaticFunction js_static_functions[] = {
1003 {"run", js_run_command, kJSPropertyAttributeNone},
1008 /* This function creates the class and its definition, only once */
1009 if (!uzbl.js.initialized) {
1010 /* it would be pretty cool to make this dynamic */
1011 uzbl.js.classdef = kJSClassDefinitionEmpty;
1012 uzbl.js.classdef.staticFunctions = js_static_functions;
1014 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1020 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1021 WebKitWebFrame *frame;
1022 JSGlobalContextRef context;
1023 JSObjectRef globalobject;
1024 JSStringRef var_name;
1026 JSStringRef js_script;
1027 JSValueRef js_result;
1028 JSStringRef js_result_string;
1029 size_t js_result_size;
1033 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1034 context = webkit_web_frame_get_global_context(frame);
1035 globalobject = JSContextGetGlobalObject(context);
1037 /* uzbl javascript namespace */
1038 var_name = JSStringCreateWithUTF8CString("Uzbl");
1039 JSObjectSetProperty(context, globalobject, var_name,
1040 JSObjectMake(context, uzbl.js.classref, NULL),
1041 kJSClassAttributeNone, NULL);
1043 /* evaluate the script and get return value*/
1044 js_script = JSStringCreateWithUTF8CString(script);
1045 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1046 if (js_result && !JSValueIsUndefined(context, js_result)) {
1047 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1048 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1050 if (js_result_size) {
1051 char js_result_utf8[js_result_size];
1052 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1053 g_string_assign(result, js_result_utf8);
1056 JSStringRelease(js_result_string);
1060 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1062 JSStringRelease(var_name);
1063 JSStringRelease(js_script);
1067 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1068 if (argv_idx(argv, 0))
1069 eval_js(web_view, argv_idx(argv, 0), result);
1073 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1075 if (argv_idx(argv, 0)) {
1076 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1081 while ((line = g_array_index(lines, gchar*, i))) {
1083 js = g_strdup (line);
1085 gchar* newjs = g_strconcat (js, line, NULL);
1092 if (uzbl.state.verbose)
1093 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1095 if (argv_idx (argv, 1)) {
1096 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1100 eval_js (web_view, js, result);
1102 g_array_free (lines, TRUE);
1107 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1108 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1109 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1110 webkit_web_view_unmark_text_matches (page);
1111 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1112 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1116 if (uzbl.state.searchtx) {
1117 if (uzbl.state.verbose)
1118 printf ("Searching: %s\n", uzbl.state.searchtx);
1119 webkit_web_view_set_highlight_text_matches (page, TRUE);
1120 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1125 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1127 search_text(page, argv, TRUE);
1131 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1133 search_text(page, argv, FALSE);
1137 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1138 (void) argv; (void) result;
1139 webkit_web_view_set_highlight_text_matches (page, FALSE);
1144 new_window_load_uri (const gchar * uri) {
1145 if (uzbl.behave.new_window) {
1146 GString *s = g_string_new ("");
1147 g_string_printf(s, "'%s'", uri);
1148 run_handler(uzbl.behave.new_window, s->str);
1151 GString* to_execute = g_string_new ("");
1152 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1154 for (i = 0; entries[i].long_name != NULL; i++) {
1155 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1156 gchar** str = (gchar**)entries[i].arg_data;
1158 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1162 if (uzbl.state.verbose)
1163 printf("\n%s\n", to_execute->str);
1164 g_spawn_command_line_async (to_execute->str, NULL);
1165 g_string_free (to_execute, TRUE);
1169 chain (WebKitWebView *page, GArray *argv, GString *result) {
1170 (void) page; (void) result;
1172 gchar **parts = NULL;
1174 while ((a = argv_idx(argv, i++))) {
1175 parts = g_strsplit (a, " ", 2);
1177 parse_command(parts[0], parts[1], result);
1183 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1187 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1193 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1197 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1203 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1208 int len = strlen(uzbl.state.keycmd);
1209 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1211 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1216 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1223 /* --Statusbar functions-- */
1225 build_progressbar_ascii(int percent) {
1226 int width=uzbl.gui.sbar.progress_w;
1229 GString *bar = g_string_new("");
1231 l = (double)percent*((double)width/100.);
1232 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1234 for(i=0; i<(int)l; i++)
1235 g_string_append(bar, uzbl.gui.sbar.progress_s);
1238 g_string_append(bar, uzbl.gui.sbar.progress_u);
1240 return g_string_free(bar, FALSE);
1242 /* --End Statusbar functions-- */
1245 sharg_append(GArray *a, const gchar *str) {
1246 const gchar *s = (str ? str : "");
1247 g_array_append_val(a, s);
1250 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1252 run_command (const gchar *command, const guint npre, const gchar **args,
1253 const gboolean sync, char **output_stdout) {
1254 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1257 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1258 gchar *pid = itos(getpid());
1259 gchar *xwin = itos(uzbl.xwin);
1261 sharg_append(a, command);
1262 for (i = 0; i < npre; i++) /* add n args before the default vars */
1263 sharg_append(a, args[i]);
1264 sharg_append(a, uzbl.state.config_file);
1265 sharg_append(a, pid);
1266 sharg_append(a, xwin);
1267 sharg_append(a, uzbl.comm.fifo_path);
1268 sharg_append(a, uzbl.comm.socket_path);
1269 sharg_append(a, uzbl.state.uri);
1270 sharg_append(a, uzbl.gui.main_title);
1272 for (i = npre; i < g_strv_length((gchar**)args); i++)
1273 sharg_append(a, args[i]);
1277 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1279 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1280 NULL, NULL, output_stdout, NULL, NULL, &err);
1281 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1282 NULL, NULL, NULL, &err);
1284 if (uzbl.state.verbose) {
1285 GString *s = g_string_new("spawned:");
1286 for (i = 0; i < (a->len); i++) {
1287 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1288 g_string_append_printf(s, " %s", qarg);
1291 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1292 printf("%s\n", s->str);
1293 g_string_free(s, TRUE);
1295 printf("Stdout: %s\n", *output_stdout);
1299 g_printerr("error on run_command: %s\n", err->message);
1304 g_array_free (a, TRUE);
1309 split_quoted(const gchar* src, const gboolean unquote) {
1310 /* split on unquoted space, return array of strings;
1311 remove a layer of quotes and backslashes if unquote */
1312 if (!src) return NULL;
1314 gboolean dq = FALSE;
1315 gboolean sq = FALSE;
1316 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1317 GString *s = g_string_new ("");
1321 for (p = src; *p != '\0'; p++) {
1322 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1323 else if (*p == '\\') { g_string_append_c(s, *p++);
1324 g_string_append_c(s, *p); }
1325 else if ((*p == '"') && unquote && !sq) dq = !dq;
1326 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1328 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1329 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1331 else if ((*p == ' ') && !dq && !sq) {
1332 dup = g_strdup(s->str);
1333 g_array_append_val(a, dup);
1334 g_string_truncate(s, 0);
1335 } else g_string_append_c(s, *p);
1337 dup = g_strdup(s->str);
1338 g_array_append_val(a, dup);
1339 ret = (gchar**)a->data;
1340 g_array_free (a, FALSE);
1341 g_string_free (s, TRUE);
1346 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1347 (void)web_view; (void)result;
1348 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1349 if (argv_idx(argv, 0))
1350 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1354 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1355 (void)web_view; (void)result;
1357 if (argv_idx(argv, 0))
1358 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1359 TRUE, &uzbl.comm.sync_stdout);
1363 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1364 (void)web_view; (void)result;
1365 if (!uzbl.behave.shell_cmd) {
1366 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1371 gchar *spacer = g_strdup("");
1372 g_array_insert_val(argv, 1, spacer);
1373 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1375 for (i = 1; i < g_strv_length(cmd); i++)
1376 g_array_prepend_val(argv, cmd[i]);
1378 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1384 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1385 (void)web_view; (void)result;
1386 if (!uzbl.behave.shell_cmd) {
1387 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1392 gchar *spacer = g_strdup("");
1393 g_array_insert_val(argv, 1, spacer);
1394 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1396 for (i = 1; i < g_strv_length(cmd); i++)
1397 g_array_prepend_val(argv, cmd[i]);
1399 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1400 TRUE, &uzbl.comm.sync_stdout);
1406 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1407 (void)web_view; (void)result;
1410 struct sockaddr_un sa;
1417 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1419 /* This function could be optimised by storing a hash table of socket paths
1420 and associated connected file descriptors rather than closing and
1421 re-opening for every call. Also we could launch a script if socket connect
1424 /* First element argv[0] is path to socket. Following elements are tokens to
1425 write to the socket. We write them as a single packet with each token
1426 separated by an ASCII nul (\0). */
1428 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1433 /* copy socket path, null terminate result */
1434 sockpath = g_array_index(argv, char*, 0);
1435 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1436 sa.sun_family = AF_UNIX;
1438 /* create socket file descriptor and connect it to path */
1439 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1441 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1444 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1445 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1450 /* build request vector */
1451 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1453 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1457 for(i = 1; i < argv->len; ++i) {
1458 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1459 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1463 ret = writev(fd, iov, argv->len - 1);
1466 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1471 /* wait for a response, with a 500ms timeout */
1473 pfd.events = POLLIN;
1475 ret = poll(&pfd, 1, 500);
1477 if(ret == 0) errno = ETIMEDOUT;
1478 if(errno == EINTR) continue;
1479 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1485 /* get length of response */
1486 if(ioctl(fd, FIONREAD, &len) == -1) {
1487 g_printerr("talk_to_socket: cannot find daemon response length, "
1488 "ioctl failed (%s)\n", strerror(errno));
1493 /* if there is a response, read it */
1495 uzbl.comm.sync_stdout = g_malloc(len + 1);
1496 if(!uzbl.comm.sync_stdout) {
1497 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1501 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1503 ret = read(fd, uzbl.comm.sync_stdout, len);
1505 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1518 parse_command(const char *cmd, const char *param, GString *result) {
1521 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1523 gchar **par = split_quoted(param, TRUE);
1524 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1526 if (c->no_split) { /* don't split */
1527 sharg_append(a, param);
1529 for (i = 0; i < g_strv_length(par); i++)
1530 sharg_append(a, par[i]);
1533 if (result == NULL) {
1534 GString *result_print = g_string_new("");
1536 c->function(uzbl.gui.web_view, a, result_print);
1537 if (result_print->len)
1538 printf("%*s\n", (int)result_print->len, result_print->str);
1540 g_string_free(result_print, TRUE);
1542 c->function(uzbl.gui.web_view, a, result);
1545 g_array_free (a, TRUE);
1548 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1555 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
1556 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1557 (GType) SOUP_SESSION_PROXY_URI);
1560 suri = soup_uri_new(uzbl.net.proxy_url);
1561 g_object_set(G_OBJECT(uzbl.net.soup_session),
1562 SOUP_SESSION_PROXY_URI,
1564 soup_uri_free(suri);
1571 if(file_exists(uzbl.gui.icon)) {
1572 if (uzbl.gui.main_window)
1573 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1575 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1581 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1582 g_array_append_val (a, uzbl.state.uri);
1583 load_uri(uzbl.gui.web_view, a, NULL);
1584 g_array_free (a, TRUE);
1588 cmd_always_insert_mode() {
1589 set_insert_mode(uzbl.behave.always_insert_mode);
1595 g_object_set(G_OBJECT(uzbl.net.soup_session),
1596 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1600 cmd_max_conns_host() {
1601 g_object_set(G_OBJECT(uzbl.net.soup_session),
1602 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1607 soup_session_remove_feature
1608 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1609 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1610 /*g_free(uzbl.net.soup_logger);*/
1612 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1613 soup_session_add_feature(uzbl.net.soup_session,
1614 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1619 return webkit_web_view_get_settings(uzbl.gui.web_view);
1624 WebKitWebSettings *ws = view_settings();
1625 if (uzbl.behave.font_size > 0) {
1626 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1629 if (uzbl.behave.monospace_size > 0) {
1630 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1631 uzbl.behave.monospace_size, NULL);
1633 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1634 uzbl.behave.font_size, NULL);
1639 cmd_default_font_family() {
1640 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1641 uzbl.behave.default_font_family, NULL);
1645 cmd_monospace_font_family() {
1646 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1647 uzbl.behave.monospace_font_family, NULL);
1651 cmd_sans_serif_font_family() {
1652 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1653 uzbl.behave.sans_serif_font_family, NULL);
1657 cmd_serif_font_family() {
1658 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1659 uzbl.behave.serif_font_family, NULL);
1663 cmd_cursive_font_family() {
1664 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1665 uzbl.behave.cursive_font_family, NULL);
1669 cmd_fantasy_font_family() {
1670 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1671 uzbl.behave.fantasy_font_family, NULL);
1676 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1680 cmd_disable_plugins() {
1681 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1682 !uzbl.behave.disable_plugins, NULL);
1686 cmd_disable_scripts() {
1687 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1688 !uzbl.behave.disable_scripts, NULL);
1692 cmd_minimum_font_size() {
1693 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1694 uzbl.behave.minimum_font_size, NULL);
1697 cmd_autoload_img() {
1698 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1699 uzbl.behave.autoload_img, NULL);
1704 cmd_autoshrink_img() {
1705 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1706 uzbl.behave.autoshrink_img, NULL);
1711 cmd_enable_spellcheck() {
1712 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1713 uzbl.behave.enable_spellcheck, NULL);
1717 cmd_enable_private() {
1718 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1719 uzbl.behave.enable_private, NULL);
1724 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1725 uzbl.behave.print_bg, NULL);
1730 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1731 uzbl.behave.style_uri, NULL);
1735 cmd_resizable_txt() {
1736 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1737 uzbl.behave.resizable_txt, NULL);
1741 cmd_default_encoding() {
1742 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1743 uzbl.behave.default_encoding, NULL);
1747 cmd_enforce_96dpi() {
1748 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1749 uzbl.behave.enforce_96dpi, NULL);
1753 cmd_caret_browsing() {
1754 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1755 uzbl.behave.caret_browsing, NULL);
1759 cmd_cookie_handler() {
1760 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1761 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1762 if ((g_strcmp0(split[0], "sh") == 0) ||
1763 (g_strcmp0(split[0], "spawn") == 0)) {
1764 g_free (uzbl.behave.cookie_handler);
1765 uzbl.behave.cookie_handler =
1766 g_strdup_printf("sync_%s %s", split[0], split[1]);
1773 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1774 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1775 if ((g_strcmp0(split[0], "sh") == 0) ||
1776 (g_strcmp0(split[0], "spawn") == 0)) {
1777 g_free (uzbl.behave.new_window);
1778 uzbl.behave.new_window =
1779 g_strdup_printf("%s %s", split[0], split[1]);
1786 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1791 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1796 if(uzbl.behave.inject_html) {
1797 webkit_web_view_load_html_string (uzbl.gui.web_view,
1798 uzbl.behave.inject_html, NULL);
1807 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1808 uzbl.behave.modmask = 0;
1810 if(uzbl.behave.modkey)
1811 g_free(uzbl.behave.modkey);
1812 uzbl.behave.modkey = buf;
1814 for (i = 0; modkeys[i].key != NULL; i++) {
1815 if (g_strrstr(buf, modkeys[i].key))
1816 uzbl.behave.modmask |= modkeys[i].mask;
1822 if (*uzbl.net.useragent == ' ') {
1823 g_free (uzbl.net.useragent);
1824 uzbl.net.useragent = NULL;
1826 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1827 uzbl.net.useragent, NULL);
1833 if (!uzbl.gui.scrolled_win &&
1837 gtk_widget_ref(uzbl.gui.scrolled_win);
1838 gtk_widget_ref(uzbl.gui.mainbar);
1839 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1840 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1842 if(uzbl.behave.status_top) {
1843 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1844 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1847 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1848 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1850 gtk_widget_unref(uzbl.gui.scrolled_win);
1851 gtk_widget_unref(uzbl.gui.mainbar);
1852 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1857 set_var_value(const gchar *name, gchar *val) {
1858 uzbl_cmdprop *c = NULL;
1861 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1863 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1864 if(!c->writeable) return FALSE;
1866 /* check for the variable type */
1867 if (c->type == TYPE_STR) {
1868 buf = expand(val, 0);
1871 } else if(c->type == TYPE_INT) {
1872 buf = expand(val, 0);
1873 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1875 } else if (c->type == TYPE_FLOAT) {
1876 buf = expand(val, 0);
1877 *c->ptr.f = strtod(buf, &endp);
1881 /* invoke a command specific function */
1882 if(c->func) c->func();
1884 /* check wether name violates our naming scheme */
1885 if(strpbrk(name, invalid_chars)) {
1886 if (uzbl.state.verbose)
1887 printf("Invalid variable name\n");
1892 c = malloc(sizeof(uzbl_cmdprop));
1897 buf = expand(val, 0);
1898 c->ptr.s = malloc(sizeof(char *));
1900 g_hash_table_insert(uzbl.comm.proto_var,
1901 g_strdup(name), (gpointer) c);
1906 enum {M_CMD, M_HTML};
1908 parse_cmd_line(const char *ctl_line, GString *result) {
1911 if((ctl_line[0] == '#') /* Comments */
1912 || (ctl_line[0] == ' ')
1913 || (ctl_line[0] == '\n'))
1914 ; /* ignore these lines */
1915 else { /* parse a command */
1917 gchar **tokens = NULL;
1918 len = strlen(ctl_line);
1920 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1921 ctlstrip = g_strndup(ctl_line, len - 1);
1922 else ctlstrip = g_strdup(ctl_line);
1924 tokens = g_strsplit(ctlstrip, " ", 2);
1925 parse_command(tokens[0], tokens[1], result);
1932 build_stream_name(int type, const gchar* dir) {
1933 State *s = &uzbl.state;
1937 str = g_strdup_printf
1938 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1939 } else if (type == SOCKET) {
1940 str = g_strdup_printf
1941 ("%s/uzbl_socket_%s", dir, s->instance_name);
1947 control_fifo(GIOChannel *gio, GIOCondition condition) {
1948 if (uzbl.state.verbose)
1949 printf("triggered\n");
1954 if (condition & G_IO_HUP)
1955 g_error ("Fifo: Read end of pipe died!\n");
1958 g_error ("Fifo: GIOChannel broke\n");
1960 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1961 if (ret == G_IO_STATUS_ERROR) {
1962 g_error ("Fifo: Error reading: %s\n", err->message);
1966 parse_cmd_line(ctl_line, NULL);
1973 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1974 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1975 if (unlink(uzbl.comm.fifo_path) == -1)
1976 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1977 g_free(uzbl.comm.fifo_path);
1978 uzbl.comm.fifo_path = NULL;
1981 GIOChannel *chan = NULL;
1982 GError *error = NULL;
1983 gchar *path = build_stream_name(FIFO, dir);
1985 if (!file_exists(path)) {
1986 if (mkfifo (path, 0666) == 0) {
1987 // 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.
1988 chan = g_io_channel_new_file(path, "r+", &error);
1990 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1991 if (uzbl.state.verbose)
1992 printf ("init_fifo: created successfully as %s\n", path);
1993 uzbl.comm.fifo_path = path;
1995 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1996 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1997 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1998 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
2000 /* if we got this far, there was an error; cleanup */
2001 if (error) g_error_free (error);
2008 control_stdin(GIOChannel *gio, GIOCondition condition) {
2010 gchar *ctl_line = NULL;
2013 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2014 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2017 parse_cmd_line(ctl_line, NULL);
2025 GIOChannel *chan = NULL;
2026 GError *error = NULL;
2028 chan = g_io_channel_unix_new(fileno(stdin));
2030 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2031 g_error ("Stdin: could not add watch\n");
2033 if (uzbl.state.verbose)
2034 printf ("Stdin: watch added successfully\n");
2037 g_error ("Stdin: Error while opening: %s\n", error->message);
2039 if (error) g_error_free (error);
2043 control_socket(GIOChannel *chan) {
2044 struct sockaddr_un remote;
2045 unsigned int t = sizeof(remote);
2047 GIOChannel *clientchan;
2049 clientsock = accept (g_io_channel_unix_get_fd(chan),
2050 (struct sockaddr *) &remote, &t);
2052 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2053 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2054 (GIOFunc) control_client_socket, clientchan);
2061 control_client_socket(GIOChannel *clientchan) {
2063 GString *result = g_string_new("");
2064 GError *error = NULL;
2068 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2069 if (ret == G_IO_STATUS_ERROR) {
2070 g_warning ("Error reading: %s\n", error->message);
2071 g_io_channel_shutdown(clientchan, TRUE, &error);
2073 } else if (ret == G_IO_STATUS_EOF) {
2074 /* shutdown and remove channel watch from main loop */
2075 g_io_channel_shutdown(clientchan, TRUE, &error);
2080 parse_cmd_line (ctl_line, result);
2081 g_string_append_c(result, '\n');
2082 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2084 if (ret == G_IO_STATUS_ERROR) {
2085 g_warning ("Error writing: %s", error->message);
2087 g_io_channel_flush(clientchan, &error);
2090 if (error) g_error_free (error);
2091 g_string_free(result, TRUE);
2097 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2098 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2099 if (unlink(uzbl.comm.socket_path) == -1)
2100 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2101 g_free(uzbl.comm.socket_path);
2102 uzbl.comm.socket_path = NULL;
2110 GIOChannel *chan = NULL;
2112 struct sockaddr_un local;
2113 gchar *path = build_stream_name(SOCKET, dir);
2115 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2117 local.sun_family = AF_UNIX;
2118 strcpy (local.sun_path, path);
2119 unlink (local.sun_path);
2121 len = strlen (local.sun_path) + sizeof (local.sun_family);
2122 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2123 if (uzbl.state.verbose)
2124 printf ("init_socket: opened in %s\n", path);
2127 if( (chan = g_io_channel_unix_new(sock)) ) {
2128 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2129 uzbl.comm.socket_path = path;
2132 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2134 /* if we got this far, there was an error; cleanup */
2141 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2142 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2144 // this function may be called very early when the templates are not set (yet), hence the checks
2146 update_title (void) {
2147 Behaviour *b = &uzbl.behave;
2150 if (b->show_status) {
2151 if (b->title_format_short) {
2152 parsed = expand(b->title_format_short, 0);
2153 if (uzbl.gui.main_window)
2154 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2157 if (b->status_format) {
2158 parsed = expand(b->status_format, 0);
2159 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2162 if (b->status_background) {
2164 gdk_color_parse (b->status_background, &color);
2165 //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)
2166 if (uzbl.gui.main_window)
2167 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2168 else if (uzbl.gui.plug)
2169 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2172 if (b->title_format_long) {
2173 parsed = expand(b->title_format_long, 0);
2174 if (uzbl.gui.main_window)
2175 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2182 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2186 retrieve_geometry();
2191 key_press_cb (GtkWidget* window, GdkEventKey* event)
2193 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2197 if (event->type != GDK_KEY_PRESS ||
2198 event->keyval == GDK_Page_Up ||
2199 event->keyval == GDK_Page_Down ||
2200 event->keyval == GDK_Up ||
2201 event->keyval == GDK_Down ||
2202 event->keyval == GDK_Left ||
2203 event->keyval == GDK_Right ||
2204 event->keyval == GDK_Shift_L ||
2205 event->keyval == GDK_Shift_R)
2208 /* turn off insert mode (if always_insert_mode is not used) */
2209 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2210 set_insert_mode(uzbl.behave.always_insert_mode);
2215 if (uzbl.behave.insert_mode &&
2216 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2217 (!uzbl.behave.modmask)
2222 if (event->keyval == GDK_Escape) {
2225 dehilight(uzbl.gui.web_view, NULL, NULL);
2229 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2230 if (event->keyval == GDK_Insert) {
2232 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2233 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2235 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2238 GString* keycmd = g_string_new(uzbl.state.keycmd);
2239 g_string_append (keycmd, str);
2240 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2247 if (event->keyval == GDK_BackSpace)
2248 keycmd_bs(NULL, NULL, NULL);
2250 gboolean key_ret = FALSE;
2251 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2254 GString* keycmd = g_string_new(uzbl.state.keycmd);
2255 g_string_append(keycmd, event->string);
2256 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2259 run_keycmd(key_ret);
2261 if (key_ret) return (!uzbl.behave.insert_mode);
2266 run_keycmd(const gboolean key_ret) {
2267 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2269 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2271 parse_command(act->name, act->param, NULL);
2275 /* try if it's an incremental keycmd or one that takes args, and run it */
2276 GString* short_keys = g_string_new ("");
2277 GString* short_keys_inc = g_string_new ("");
2279 guint len = strlen(uzbl.state.keycmd);
2280 for (i=0; i<len; i++) {
2281 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2282 g_string_assign(short_keys_inc, short_keys->str);
2283 g_string_append_c(short_keys, '_');
2284 g_string_append_c(short_keys_inc, '*');
2286 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2287 /* run normal cmds only if return was pressed */
2288 exec_paramcmd(act, i);
2291 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2292 if (key_ret) /* just quit the incremental command on return */
2294 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2298 g_string_truncate(short_keys, short_keys->len - 1);
2300 g_string_free (short_keys, TRUE);
2301 g_string_free (short_keys_inc, TRUE);
2305 exec_paramcmd(const Action *act, const guint i) {
2306 GString *parampart = g_string_new (uzbl.state.keycmd);
2307 GString *actionname = g_string_new ("");
2308 GString *actionparam = g_string_new ("");
2309 g_string_erase (parampart, 0, i+1);
2311 g_string_printf (actionname, act->name, parampart->str);
2313 g_string_printf (actionparam, act->param, parampart->str);
2314 parse_command(actionname->str, actionparam->str, NULL);
2315 g_string_free(actionname, TRUE);
2316 g_string_free(actionparam, TRUE);
2317 g_string_free(parampart, TRUE);
2325 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2327 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2328 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2329 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2330 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2331 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2332 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2333 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2334 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2335 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2336 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2337 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2344 g->mainbar = gtk_hbox_new (FALSE, 0);
2346 /* keep a reference to the bar so we can re-pack it at runtime*/
2347 //sbar_ref = g_object_ref(g->mainbar);
2349 g->mainbar_label = gtk_label_new ("");
2350 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2351 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2352 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2353 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2354 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2355 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2361 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2362 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2363 gtk_widget_set_name (window, "Uzbl browser");
2364 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2365 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2366 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2373 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2374 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2375 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2382 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2384 If actname is one that calls an external command, this function will inject
2385 newargs in front of the user-provided args in that command line. They will
2386 come become after the body of the script (in sh) or after the name of
2387 the command to execute (in spawn).
2388 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2389 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2391 The return value consist of two strings: the action (sh, ...) and its args.
2393 If act is not one that calls an external command, then the given action merely
2396 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2397 /* Arrr! Here be memory leaks */
2398 gchar *actdup = g_strdup(actname);
2399 g_array_append_val(rets, actdup);
2401 if ((g_strcmp0(actname, "spawn") == 0) ||
2402 (g_strcmp0(actname, "sh") == 0) ||
2403 (g_strcmp0(actname, "sync_spawn") == 0) ||
2404 (g_strcmp0(actname, "sync_sh") == 0) ||
2405 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2407 GString *a = g_string_new("");
2408 gchar **spawnparts = split_quoted(origargs, FALSE);
2409 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2410 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2412 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2413 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2415 g_array_append_val(rets, a->str);
2416 g_string_free(a, FALSE);
2417 g_strfreev(spawnparts);
2419 gchar *origdup = g_strdup(origargs);
2420 g_array_append_val(rets, origdup);
2422 return (gchar**)g_array_free(rets, FALSE);
2426 run_handler (const gchar *act, const gchar *args) {
2427 /* Consider this code a temporary hack to make the handlers usable.
2428 In practice, all this splicing, injection, and reconstruction is
2429 inefficient, annoying and hard to manage. Potential pitfalls arise
2430 when the handler specific args 1) are not quoted (the handler
2431 callbacks should take care of this) 2) are quoted but interfere
2432 with the users' own quotation. A more ideal solution is
2433 to refactor parse_command so that it doesn't just take a string
2434 and execute it; rather than that, we should have a function which
2435 returns the argument vector parsed from the string. This vector
2436 could be modified (e.g. insert additional args into it) before
2437 passing it to the next function that actually executes it. Though
2438 it still isn't perfect for chain actions.. will reconsider & re-
2439 factor when I have the time. -duc */
2441 char **parts = g_strsplit(act, " ", 2);
2443 if (g_strcmp0(parts[0], "chain") == 0) {
2444 GString *newargs = g_string_new("");
2445 gchar **chainparts = split_quoted(parts[1], FALSE);
2447 /* for every argument in the chain, inject the handler args
2448 and make sure the new parts are wrapped in quotes */
2449 gchar **cp = chainparts;
2451 gchar *quotless = NULL;
2452 gchar **spliced_quotless = NULL; // sigh -_-;
2453 gchar **inpart = NULL;
2456 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2458 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2459 } else quotless = g_strdup(*cp);
2461 spliced_quotless = g_strsplit(quotless, " ", 2);
2462 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2463 g_strfreev(spliced_quotless);
2465 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2471 parse_command(parts[0], &(newargs->str[1]), NULL);
2472 g_string_free(newargs, TRUE);
2473 g_strfreev(chainparts);
2476 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2477 parse_command(inparts[0], inparts[1], NULL);
2485 add_binding (const gchar *key, const gchar *act) {
2486 char **parts = g_strsplit(act, " ", 2);
2493 if (uzbl.state.verbose)
2494 printf ("Binding %-10s : %s\n", key, act);
2495 action = new_action(parts[0], parts[1]);
2497 if (g_hash_table_remove (uzbl.bindings, key))
2498 g_warning ("Overwriting existing binding for \"%s\"", key);
2499 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2504 get_xdg_var (XDG_Var xdg) {
2505 const gchar* actual_value = getenv (xdg.environmental);
2506 const gchar* home = getenv ("HOME");
2507 gchar* return_value;
2509 if (! actual_value || strcmp (actual_value, "") == 0) {
2510 if (xdg.default_value) {
2511 return_value = str_replace ("~", home, xdg.default_value);
2513 return_value = NULL;
2516 return_value = str_replace("~", home, actual_value);
2519 return return_value;
2523 find_xdg_file (int xdg_type, const char* filename) {
2524 /* xdg_type = 0 => config
2525 xdg_type = 1 => data
2526 xdg_type = 2 => cache*/
2528 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2529 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2532 gchar* temporary_string;
2536 if (! file_exists (temporary_file) && xdg_type != 2) {
2537 buf = get_xdg_var (XDG[3 + xdg_type]);
2538 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2541 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2542 g_free (temporary_file);
2543 temporary_file = g_strconcat (temporary_string, filename, NULL);
2547 //g_free (temporary_string); - segfaults.
2549 if (file_exists (temporary_file)) {
2550 return temporary_file;
2552 g_free(temporary_file);
2558 State *s = &uzbl.state;
2559 Network *n = &uzbl.net;
2561 for (i = 0; default_config[i].command != NULL; i++) {
2562 parse_cmd_line(default_config[i].command, NULL);
2565 if (g_strcmp0(s->config_file, "-") == 0) {
2566 s->config_file = NULL;
2570 else if (!s->config_file) {
2571 s->config_file = find_xdg_file (0, "/uzbl/config");
2574 if (s->config_file) {
2575 GArray* lines = read_file_by_line (s->config_file);
2579 while ((line = g_array_index(lines, gchar*, i))) {
2580 parse_cmd_line (line, NULL);
2584 g_array_free (lines, TRUE);
2586 if (uzbl.state.verbose)
2587 printf ("No configuration file loaded.\n");
2590 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2593 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2596 if (!uzbl.behave.cookie_handler)
2599 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2600 GString *s = g_string_new ("");
2601 SoupURI * soup_uri = soup_message_get_uri(msg);
2602 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2603 run_handler(uzbl.behave.cookie_handler, s->str);
2605 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2606 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2607 if ( p != NULL ) *p = '\0';
2608 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2610 if (uzbl.comm.sync_stdout)
2611 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2613 g_string_free(s, TRUE);
2617 save_cookies (SoupMessage *msg, gpointer user_data){
2621 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2622 cookie = soup_cookie_to_set_cookie_header(ck->data);
2623 SoupURI * soup_uri = soup_message_get_uri(msg);
2624 GString *s = g_string_new ("");
2625 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2626 run_handler(uzbl.behave.cookie_handler, s->str);
2628 g_string_free(s, TRUE);
2633 /* --- WEBINSPECTOR --- */
2635 hide_window_cb(GtkWidget *widget, gpointer data) {
2638 gtk_widget_hide(widget);
2642 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2645 (void) web_inspector;
2646 GtkWidget* scrolled_window;
2647 GtkWidget* new_web_view;
2650 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2651 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2652 G_CALLBACK(hide_window_cb), NULL);
2654 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2655 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2656 gtk_widget_show(g->inspector_window);
2658 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2659 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2660 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2661 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2662 gtk_widget_show(scrolled_window);
2664 new_web_view = webkit_web_view_new();
2665 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2667 return WEBKIT_WEB_VIEW(new_web_view);
2671 inspector_show_window_cb (WebKitWebInspector* inspector){
2673 gtk_widget_show(uzbl.gui.inspector_window);
2677 /* TODO: Add variables and code to make use of these functions */
2679 inspector_close_window_cb (WebKitWebInspector* inspector){
2685 inspector_attach_window_cb (WebKitWebInspector* inspector){
2691 inspector_detach_window_cb (WebKitWebInspector* inspector){
2697 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2703 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2709 set_up_inspector() {
2711 WebKitWebSettings *settings = view_settings();
2712 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2714 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2715 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2716 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2717 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2718 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2719 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2720 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2722 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2726 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2728 uzbl_cmdprop *c = v;
2733 if(c->type == TYPE_STR)
2734 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2735 else if(c->type == TYPE_INT)
2736 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2737 else if(c->type == TYPE_FLOAT)
2738 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2742 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2746 printf("bind %s = %s %s\n", (char *)k ,
2747 (char *)a->name, a->param?(char *)a->param:"");
2752 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2753 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2757 retrieve_geometry() {
2759 GString *buf = g_string_new("");
2761 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2762 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2764 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2766 if(uzbl.gui.geometry)
2767 g_free(uzbl.gui.geometry);
2768 uzbl.gui.geometry = g_string_free(buf, FALSE);
2771 /* set up gtk, gobject, variable defaults and other things that tests and other
2772 * external applications need to do anyhow */
2774 initialize(int argc, char *argv[]) {
2775 if (!g_thread_supported ())
2776 g_thread_init (NULL);
2777 uzbl.state.executable_path = g_strdup(argv[0]);
2778 uzbl.state.selected_url = NULL;
2779 uzbl.state.searchtx = NULL;
2781 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2782 g_option_context_add_main_entries (context, entries, NULL);
2783 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2784 g_option_context_parse (context, &argc, &argv, NULL);
2785 g_option_context_free(context);
2787 if (uzbl.behave.print_version) {
2788 printf("Commit: %s\n", COMMIT);
2792 /* initialize hash table */
2793 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2795 uzbl.net.soup_session = webkit_get_default_session();
2796 uzbl.state.keycmd = g_strdup("");
2798 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2799 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2800 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2801 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2803 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2804 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2805 uzbl.gui.sbar.progress_w = 10;
2807 /* default mode indicators */
2808 uzbl.behave.insert_indicator = g_strdup("I");
2809 uzbl.behave.cmd_indicator = g_strdup("C");
2811 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2812 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2813 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2814 uzbl.info.arch = ARCH;
2815 uzbl.info.commit = COMMIT;
2818 make_var_to_name_hash();
2823 #ifndef UZBL_LIBRARY
2826 main (int argc, char* argv[]) {
2827 initialize(argc, argv);
2829 gtk_init (&argc, &argv);
2831 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2832 //main_window_ref = g_object_ref(scrolled_window);
2833 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2834 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2836 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2837 GTK_WIDGET (uzbl.gui.web_view));
2839 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2843 /* initial packing */
2844 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2845 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2847 if (uzbl.state.socket_id) {
2848 uzbl.gui.plug = create_plug ();
2849 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2850 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2852 uzbl.gui.main_window = create_window ();
2853 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2854 gtk_widget_show_all (uzbl.gui.main_window);
2855 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2858 if(!uzbl.state.instance_name)
2859 uzbl.state.instance_name = itos((int)uzbl.xwin);
2861 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2863 if (uzbl.state.verbose) {
2864 printf("Uzbl start location: %s\n", argv[0]);
2865 if (uzbl.state.socket_id)
2866 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2868 printf("window_id %i\n",(int) uzbl.xwin);
2869 printf("pid %i\n", getpid ());
2870 printf("name: %s\n", uzbl.state.instance_name);
2873 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2874 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2875 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2876 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2877 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2879 /* Check uzbl is in window mode before getting/setting geometry */
2880 if (uzbl.gui.main_window) {
2881 if(uzbl.gui.geometry)
2884 retrieve_geometry();
2887 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2888 if (argc > 1 && !uzbl.state.uri)
2889 uri_override = g_strdup(argv[1]);
2890 gboolean verbose_override = uzbl.state.verbose;
2893 set_insert_mode(FALSE);
2895 if (!uzbl.behave.show_status)
2896 gtk_widget_hide(uzbl.gui.mainbar);
2903 if (verbose_override > uzbl.state.verbose)
2904 uzbl.state.verbose = verbose_override;
2907 set_var_value("uri", uri_override);
2908 g_free(uri_override);
2909 } else if (uzbl.state.uri)
2915 return EXIT_SUCCESS;
2919 /* vi: set et ts=4: */