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>
58 #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 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
88 /* associate command names to their properties */
89 typedef const struct {
90 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
91 the PTR() macro is kind of preventing this change at the moment. */
100 /*@null@*/ void (*func)(void);
103 /* abbreviations to help keep the table's width humane */
104 #define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun }
105 #define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun }
106 #define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun }
107 #define PTR_C_STR(var, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun }
108 #define PTR_C_INT(var, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun }
109 #define PTR_C_FLOAT(var, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun }
114 } var_name_to_ptr[] = {
115 /* variable name pointer to variable in code dump callback function */
116 /* ---------------------------------------------------------------------------------------------- */
117 { "uri", PTR_V_STR(uzbl.state.uri, 1, cmd_load_uri)},
118 { "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)},
119 { "mode", PTR_V_INT(uzbl.behave.mode, 0, NULL)},
120 { "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)},
121 { "base_url", PTR_V_STR(uzbl.behave.base_url, 1, NULL)},
122 { "html_endmarker", PTR_V_STR(uzbl.behave.html_endmarker, 1, NULL)},
123 { "html_mode_timeout", PTR_V_INT(uzbl.behave.html_timeout, 1, NULL)},
124 { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
125 { "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
126 { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
127 { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
128 { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
129 { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
130 { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
131 { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
132 { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
133 { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
134 { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
135 { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
136 { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
137 { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
138 { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
139 { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
140 { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
141 { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
142 { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
143 { "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
144 { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
145 { "history_handler", PTR_V_STR(uzbl.behave.history_handler, 1, NULL)},
146 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
147 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
148 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
149 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
150 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
151 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
152 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
153 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
154 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
155 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
156 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
158 /* exported WebKitWebSettings properties */
159 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
160 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
161 { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)},
162 { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)},
163 { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)},
164 { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)},
165 { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)},
166 { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)},
167 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
168 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
169 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
170 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
171 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
172 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
173 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
174 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
175 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
176 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
177 { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
178 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
179 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
180 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
182 /* constants (not dumpable or writeable) */
183 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
184 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
185 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
186 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
187 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
188 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
189 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
190 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
191 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
192 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
193 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
195 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
196 }, *n2v_p = var_name_to_ptr;
200 /*@null@*/ char *key;
203 { "SHIFT", GDK_SHIFT_MASK }, // shift
204 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
205 { "CONTROL", GDK_CONTROL_MASK }, // control
206 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
207 { "MOD2", GDK_MOD2_MASK }, // 5th mod
208 { "MOD3", GDK_MOD3_MASK }, // 6th mod
209 { "MOD4", GDK_MOD4_MASK }, // 7th mod
210 { "MOD5", GDK_MOD5_MASK }, // 8th mod
211 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
212 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
213 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
214 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
215 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
216 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
217 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
218 { "META", GDK_META_MASK }, // meta (since 2.10)
223 /* construct a hash from the var_name_to_ptr array for quick access */
225 make_var_to_name_hash() {
226 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
228 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
233 /* --- UTILITY FUNCTIONS --- */
234 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
236 get_exp_type(const gchar *s) {
240 else if(*(s+1) == '{')
241 return EXP_BRACED_VAR;
242 else if(*(s+1) == '<')
244 else if(*(s+1) == '[')
247 return EXP_SIMPLE_VAR;
254 * recurse == 1: don't expand '@(command)@'
255 * recurse == 2: don't expand '@<java script>@'
258 expand(const char *s, guint recurse) {
261 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
265 gchar *cmd_stdout = NULL;
267 GString *buf = g_string_new("");
268 GString *js_ret = g_string_new("");
273 g_string_append_c(buf, *++s);
278 etype = get_exp_type(s);
283 vend = strpbrk(s, end_simple_var);
284 if(!vend) vend = strchr(s, '\0');
288 vend = strchr(s, '}');
289 if(!vend) vend = strchr(s, '\0');
293 vend = strstr(s, ")@");
294 if(!vend) vend = strchr(s, '\0');
298 vend = strstr(s, ">@");
299 if(!vend) vend = strchr(s, '\0');
303 vend = strstr(s, "]@");
304 if(!vend) vend = strchr(s, '\0');
309 ret = g_strndup(s, vend-s);
311 if(etype == EXP_SIMPLE_VAR ||
312 etype == EXP_BRACED_VAR) {
313 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
314 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
315 g_string_append(buf, (gchar *)*c->ptr.s);
316 } else if(c->type == TYPE_INT) {
317 g_string_append_printf(buf, "%d", *c->ptr.i);
319 else if(c->type == TYPE_FLOAT) {
320 g_string_append_printf(buf, "%f", *c->ptr.f);
324 if(etype == EXP_SIMPLE_VAR)
329 else if(recurse != 1 &&
331 mycmd = expand(ret, 1);
332 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
336 g_printerr("error on running command: %s\n", err->message);
339 else if (*cmd_stdout) {
340 size_t len = strlen(cmd_stdout);
342 if(len > 0 && cmd_stdout[len-1] == '\n')
343 cmd_stdout[--len] = '\0'; /* strip trailing newline */
345 g_string_append(buf, cmd_stdout);
350 else if(recurse != 2 &&
352 mycmd = expand(ret, 2);
353 eval_js(uzbl.gui.web_view, mycmd, js_ret);
357 g_string_append(buf, js_ret->str);
358 g_string_free(js_ret, TRUE);
359 js_ret = g_string_new("");
363 else if(etype == EXP_ESCAPE) {
364 mycmd = expand(ret, 0);
365 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
367 g_string_append(buf, escaped);
379 g_string_append_c(buf, *s);
384 g_string_free(js_ret, TRUE);
385 return g_string_free(buf, FALSE);
392 snprintf(tmp, sizeof(tmp), "%i", val);
393 return g_strdup(tmp);
397 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
400 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
403 str_replace (const char* search, const char* replace, const char* string) {
407 buf = g_strsplit (string, search, -1);
408 ret = g_strjoinv (replace, buf);
409 g_strfreev(buf); // somebody said this segfaults
415 read_file_by_line (gchar *path) {
416 GIOChannel *chan = NULL;
417 gchar *readbuf = NULL;
419 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
422 chan = g_io_channel_new_file(path, "r", NULL);
425 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
426 const gchar* val = g_strdup (readbuf);
427 g_array_append_val (lines, val);
432 g_io_channel_unref (chan);
434 fprintf(stderr, "File '%s' not be read.\n", path);
441 parseenv (char* string) {
442 extern char** environ;
443 gchar* tmpstr = NULL;
447 while (environ[i] != NULL) {
448 gchar** env = g_strsplit (environ[i], "=", 2);
449 gchar* envname = g_strconcat ("$", env[0], NULL);
451 if (g_strrstr (string, envname) != NULL) {
452 tmpstr = g_strdup(string);
454 string = str_replace(envname, env[1], tmpstr);
459 g_strfreev (env); // somebody said this breaks uzbl
467 setup_signal(int signr, sigfunc *shandler) {
468 struct sigaction nh, oh;
470 nh.sa_handler = shandler;
471 sigemptyset(&nh.sa_mask);
474 if(sigaction(signr, &nh, &oh) < 0)
482 if (uzbl.behave.fifo_dir)
483 unlink (uzbl.comm.fifo_path);
484 if (uzbl.behave.socket_dir)
485 unlink (uzbl.comm.socket_path);
487 g_free(uzbl.state.executable_path);
488 g_free(uzbl.state.keycmd);
489 g_hash_table_destroy(uzbl.bindings);
490 g_hash_table_destroy(uzbl.behave.commands);
493 /* used for html_mode_timeout
494 * be sure to extend this function to use
495 * more timers if needed in other places
498 set_timeout(int seconds) {
500 memset(&t, 0, sizeof t);
502 t.it_value.tv_sec = seconds;
503 t.it_value.tv_usec = 0;
504 setitimer(ITIMER_REAL, &t, NULL);
507 /* --- SIGNAL HANDLER --- */
510 catch_sigterm(int s) {
516 catch_sigint(int s) {
526 set_var_value("mode", "0");
531 /* --- CALLBACKS --- */
534 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
537 (void) navigation_action;
538 (void) policy_decision;
540 const gchar* uri = webkit_network_request_get_uri (request);
541 if (uzbl.state.verbose)
542 printf("New window requested -> %s \n", uri);
543 webkit_web_policy_decision_use(policy_decision);
548 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
553 /* If we can display it, let's display it... */
554 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
555 webkit_web_policy_decision_use (policy_decision);
559 /* ...everything we can't displayed is downloaded */
560 webkit_web_policy_decision_download (policy_decision);
565 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
569 if (uzbl.state.selected_url != NULL) {
570 if (uzbl.state.verbose)
571 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
572 new_window_load_uri(uzbl.state.selected_url);
574 if (uzbl.state.verbose)
575 printf("New web view -> %s\n","Nothing to open, exiting");
581 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
584 if (uzbl.behave.download_handler) {
585 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
586 if (uzbl.state.verbose)
587 printf("Download -> %s\n",uri);
588 /* if urls not escaped, we may have to escape and quote uri before this call */
589 run_handler(uzbl.behave.download_handler, uri);
594 /* scroll a bar in a given direction */
596 scroll (GtkAdjustment* bar, GArray *argv) {
600 gdouble page_size = gtk_adjustment_get_page_size(bar);
601 gdouble value = gtk_adjustment_get_value(bar);
602 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
605 value += page_size * amount * 0.01;
609 max_value = gtk_adjustment_get_upper(bar) - page_size;
611 if (value > max_value)
612 value = max_value; /* don't scroll past the end of the page */
614 gtk_adjustment_set_value (bar, value);
618 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
619 (void) page; (void) argv; (void) result;
620 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
624 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
625 (void) page; (void) argv; (void) result;
626 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
627 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
631 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
632 (void) page; (void) result;
633 scroll(uzbl.gui.bar_v, argv);
637 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
638 (void) page; (void) result;
639 scroll(uzbl.gui.bar_h, argv);
644 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
645 if(uzbl.state.verbose)
646 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
648 /* update geometry var with the actual geometry
649 this is necessary as some WMs don't seem to honour
650 the above setting and we don't want to end up with
651 wrong geometry information
658 if (!uzbl.behave.show_status) {
659 gtk_widget_hide(uzbl.gui.mainbar);
661 gtk_widget_show(uzbl.gui.mainbar);
667 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
672 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
676 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
681 if (uzbl.behave.show_status) {
682 gtk_widget_hide(uzbl.gui.mainbar);
684 gtk_widget_show(uzbl.gui.mainbar);
686 uzbl.behave.show_status = !uzbl.behave.show_status;
691 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
695 //Set selected_url state variable
696 g_free(uzbl.state.selected_url);
697 uzbl.state.selected_url = NULL;
699 uzbl.state.selected_url = g_strdup(link);
705 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
708 const gchar *title = webkit_web_view_get_title(web_view);
709 if (uzbl.gui.main_title)
710 g_free (uzbl.gui.main_title);
711 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
716 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
719 uzbl.gui.sbar.load_progress = progress;
721 g_free(uzbl.gui.sbar.progress_bar);
722 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
728 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
732 if (uzbl.behave.load_finish_handler)
733 run_handler(uzbl.behave.load_finish_handler, "");
736 void clear_keycmd() {
737 g_free(uzbl.state.keycmd);
738 uzbl.state.keycmd = g_strdup("");
742 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
746 uzbl.gui.sbar.load_progress = 0;
747 clear_keycmd(); // don't need old commands to remain on new page?
748 if (uzbl.behave.load_start_handler)
749 run_handler(uzbl.behave.load_start_handler, "");
753 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
756 g_free (uzbl.state.uri);
757 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
758 uzbl.state.uri = g_string_free (newuri, FALSE);
759 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
760 set_insert_mode(uzbl.behave.always_insert_mode);
763 if (uzbl.behave.load_commit_handler)
764 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
768 destroy_cb (GtkWidget* widget, gpointer data) {
776 if (uzbl.behave.history_handler) {
778 struct tm * timeinfo;
781 timeinfo = localtime ( &rawtime );
782 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
783 run_handler(uzbl.behave.history_handler, date);
788 /* VIEW funcs (little webkit wrappers) */
789 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
791 VIEWFUNC(reload_bypass_cache)
792 VIEWFUNC(stop_loading)
799 /* -- command to callback/function map for things we cannot attach to any signals */
800 struct {char *key; CommandInfo value;} cmdlist[] =
801 { /* key function no_split */
802 { "back", {view_go_back, 0} },
803 { "forward", {view_go_forward, 0} },
804 { "scroll_vert", {scroll_vert, 0} },
805 { "scroll_horz", {scroll_horz, 0} },
806 { "scroll_begin", {scroll_begin, 0} },
807 { "scroll_end", {scroll_end, 0} },
808 { "reload", {view_reload, 0}, },
809 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
810 { "stop", {view_stop_loading, 0}, },
811 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
812 { "zoom_out", {view_zoom_out, 0}, },
813 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
814 { "uri", {load_uri, TRUE} },
815 { "js", {run_js, TRUE} },
816 { "script", {run_external_js, 0} },
817 { "toggle_status", {toggle_status_cb, 0} },
818 { "spawn", {spawn, 0} },
819 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
820 { "sh", {spawn_sh, 0} },
821 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
822 { "talk_to_socket", {talk_to_socket, 0} },
823 { "exit", {close_uzbl, 0} },
824 { "search", {search_forward_text, TRUE} },
825 { "search_reverse", {search_reverse_text, TRUE} },
826 { "dehilight", {dehilight, 0} },
827 { "toggle_insert_mode", {toggle_insert_mode, 0} },
828 { "set", {set_var, TRUE} },
829 //{ "get", {get_var, TRUE} },
830 { "bind", {act_bind, TRUE} },
831 { "dump_config", {act_dump_config, 0} },
832 { "keycmd", {keycmd, TRUE} },
833 { "keycmd_nl", {keycmd_nl, TRUE} },
834 { "keycmd_bs", {keycmd_bs, 0} },
835 { "chain", {chain, 0} },
836 { "print", {print, TRUE} }
843 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
845 for (i = 0; i < LENGTH(cmdlist); i++)
846 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
849 /* -- CORE FUNCTIONS -- */
852 free_action(gpointer act) {
853 Action *action = (Action*)act;
854 g_free(action->name);
856 g_free(action->param);
861 new_action(const gchar *name, const gchar *param) {
862 Action *action = g_new(Action, 1);
864 action->name = g_strdup(name);
866 action->param = g_strdup(param);
868 action->param = NULL;
874 file_exists (const char * filename) {
875 return (access(filename, F_OK) == 0);
879 set_var(WebKitWebView *page, GArray *argv, GString *result) {
880 (void) page; (void) result;
881 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
882 if (split[0] != NULL) {
883 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
884 set_var_value(g_strstrip(split[0]), value);
891 print(WebKitWebView *page, GArray *argv, GString *result) {
892 (void) page; (void) result;
895 buf = expand(argv_idx(argv, 0), 0);
896 g_string_assign(result, buf);
901 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
902 (void) page; (void) result;
903 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
904 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
905 add_binding(g_strstrip(split[0]), value);
923 set_mode_indicator() {
924 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
925 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
930 set_mode_indicator();
935 set_insert_mode(gboolean mode) {
936 uzbl.behave.insert_mode = mode;
937 set_mode_indicator();
941 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
942 (void) page; (void) result;
944 if (argv_idx(argv, 0)) {
945 if (strcmp (argv_idx(argv, 0), "0") == 0) {
946 set_insert_mode(FALSE);
948 set_insert_mode(TRUE);
951 set_insert_mode( !uzbl.behave.insert_mode );
958 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
961 if (argv_idx(argv, 0)) {
962 GString* newuri = g_string_new (argv_idx(argv, 0));
963 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
964 run_js(web_view, argv, NULL);
967 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
968 g_string_prepend (newuri, "http://");
969 /* if we do handle cookies, ask our handler for them */
970 webkit_web_view_load_uri (web_view, newuri->str);
971 g_string_free (newuri, TRUE);
978 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
979 size_t argumentCount, const JSValueRef arguments[],
980 JSValueRef* exception) {
985 JSStringRef js_result_string;
986 GString *result = g_string_new("");
988 if (argumentCount >= 1) {
989 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
990 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
991 char ctl_line[arg_size];
992 JSStringGetUTF8CString(arg, ctl_line, arg_size);
994 parse_cmd_line(ctl_line, result);
996 JSStringRelease(arg);
998 js_result_string = JSStringCreateWithUTF8CString(result->str);
1000 g_string_free(result, TRUE);
1002 return JSValueMakeString(ctx, js_result_string);
1005 JSStaticFunction js_static_functions[] = {
1006 {"run", js_run_command, kJSPropertyAttributeNone},
1011 /* This function creates the class and its definition, only once */
1012 if (!uzbl.js.initialized) {
1013 /* it would be pretty cool to make this dynamic */
1014 uzbl.js.classdef = kJSClassDefinitionEmpty;
1015 uzbl.js.classdef.staticFunctions = js_static_functions;
1017 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1023 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1024 WebKitWebFrame *frame;
1025 JSGlobalContextRef context;
1026 JSObjectRef globalobject;
1027 JSStringRef var_name;
1029 JSStringRef js_script;
1030 JSValueRef js_result;
1031 JSStringRef js_result_string;
1032 size_t js_result_size;
1036 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1037 context = webkit_web_frame_get_global_context(frame);
1038 globalobject = JSContextGetGlobalObject(context);
1040 /* uzbl javascript namespace */
1041 var_name = JSStringCreateWithUTF8CString("Uzbl");
1042 JSObjectSetProperty(context, globalobject, var_name,
1043 JSObjectMake(context, uzbl.js.classref, NULL),
1044 kJSClassAttributeNone, NULL);
1046 /* evaluate the script and get return value*/
1047 js_script = JSStringCreateWithUTF8CString(script);
1048 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1049 if (js_result && !JSValueIsUndefined(context, js_result)) {
1050 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1051 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1053 if (js_result_size) {
1054 char js_result_utf8[js_result_size];
1055 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1056 g_string_assign(result, js_result_utf8);
1059 JSStringRelease(js_result_string);
1063 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1065 JSStringRelease(var_name);
1066 JSStringRelease(js_script);
1070 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1071 if (argv_idx(argv, 0))
1072 eval_js(web_view, argv_idx(argv, 0), result);
1076 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1078 if (argv_idx(argv, 0)) {
1079 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1084 while ((line = g_array_index(lines, gchar*, i))) {
1086 js = g_strdup (line);
1088 gchar* newjs = g_strconcat (js, line, NULL);
1095 if (uzbl.state.verbose)
1096 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1098 if (argv_idx (argv, 1)) {
1099 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1103 eval_js (web_view, js, result);
1105 g_array_free (lines, TRUE);
1110 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1111 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1112 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1113 webkit_web_view_unmark_text_matches (page);
1114 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1115 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1119 if (uzbl.state.searchtx) {
1120 if (uzbl.state.verbose)
1121 printf ("Searching: %s\n", uzbl.state.searchtx);
1122 webkit_web_view_set_highlight_text_matches (page, TRUE);
1123 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1128 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1130 search_text(page, argv, TRUE);
1134 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1136 search_text(page, argv, FALSE);
1140 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1141 (void) argv; (void) result;
1142 webkit_web_view_set_highlight_text_matches (page, FALSE);
1147 new_window_load_uri (const gchar * uri) {
1148 if (uzbl.behave.new_window) {
1149 GString *s = g_string_new ("");
1150 g_string_printf(s, "'%s'", uri);
1151 run_handler(uzbl.behave.new_window, s->str);
1154 GString* to_execute = g_string_new ("");
1155 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1157 for (i = 0; entries[i].long_name != NULL; i++) {
1158 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1159 gchar** str = (gchar**)entries[i].arg_data;
1161 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1165 if (uzbl.state.verbose)
1166 printf("\n%s\n", to_execute->str);
1167 g_spawn_command_line_async (to_execute->str, NULL);
1168 g_string_free (to_execute, TRUE);
1172 chain (WebKitWebView *page, GArray *argv, GString *result) {
1173 (void) page; (void) result;
1175 gchar **parts = NULL;
1177 while ((a = argv_idx(argv, i++))) {
1178 parts = g_strsplit (a, " ", 2);
1180 parse_command(parts[0], parts[1], result);
1186 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1190 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1196 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1200 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1206 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1211 int len = strlen(uzbl.state.keycmd);
1212 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1214 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1219 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1226 /* --Statusbar functions-- */
1228 build_progressbar_ascii(int percent) {
1229 int width=uzbl.gui.sbar.progress_w;
1232 GString *bar = g_string_new("");
1234 l = (double)percent*((double)width/100.);
1235 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1237 for(i=0; i<(int)l; i++)
1238 g_string_append(bar, uzbl.gui.sbar.progress_s);
1241 g_string_append(bar, uzbl.gui.sbar.progress_u);
1243 return g_string_free(bar, FALSE);
1245 /* --End Statusbar functions-- */
1248 sharg_append(GArray *a, const gchar *str) {
1249 const gchar *s = (str ? str : "");
1250 g_array_append_val(a, s);
1253 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1255 run_command (const gchar *command, const guint npre, const gchar **args,
1256 const gboolean sync, char **output_stdout) {
1257 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1260 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1261 gchar *pid = itos(getpid());
1262 gchar *xwin = itos(uzbl.xwin);
1264 sharg_append(a, command);
1265 for (i = 0; i < npre; i++) /* add n args before the default vars */
1266 sharg_append(a, args[i]);
1267 sharg_append(a, uzbl.state.config_file);
1268 sharg_append(a, pid);
1269 sharg_append(a, xwin);
1270 sharg_append(a, uzbl.comm.fifo_path);
1271 sharg_append(a, uzbl.comm.socket_path);
1272 sharg_append(a, uzbl.state.uri);
1273 sharg_append(a, uzbl.gui.main_title);
1275 for (i = npre; i < g_strv_length((gchar**)args); i++)
1276 sharg_append(a, args[i]);
1280 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1282 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1283 NULL, NULL, output_stdout, NULL, NULL, &err);
1284 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1285 NULL, NULL, NULL, &err);
1287 if (uzbl.state.verbose) {
1288 GString *s = g_string_new("spawned:");
1289 for (i = 0; i < (a->len); i++) {
1290 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1291 g_string_append_printf(s, " %s", qarg);
1294 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1295 printf("%s\n", s->str);
1296 g_string_free(s, TRUE);
1298 printf("Stdout: %s\n", *output_stdout);
1302 g_printerr("error on run_command: %s\n", err->message);
1307 g_array_free (a, TRUE);
1312 split_quoted(const gchar* src, const gboolean unquote) {
1313 /* split on unquoted space, return array of strings;
1314 remove a layer of quotes and backslashes if unquote */
1315 if (!src) return NULL;
1317 gboolean dq = FALSE;
1318 gboolean sq = FALSE;
1319 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1320 GString *s = g_string_new ("");
1324 for (p = src; *p != '\0'; p++) {
1325 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1326 else if (*p == '\\') { g_string_append_c(s, *p++);
1327 g_string_append_c(s, *p); }
1328 else if ((*p == '"') && unquote && !sq) dq = !dq;
1329 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1331 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1332 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1334 else if ((*p == ' ') && !dq && !sq) {
1335 dup = g_strdup(s->str);
1336 g_array_append_val(a, dup);
1337 g_string_truncate(s, 0);
1338 } else g_string_append_c(s, *p);
1340 dup = g_strdup(s->str);
1341 g_array_append_val(a, dup);
1342 ret = (gchar**)a->data;
1343 g_array_free (a, FALSE);
1344 g_string_free (s, TRUE);
1349 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1350 (void)web_view; (void)result;
1351 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1352 if (argv_idx(argv, 0))
1353 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1357 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1358 (void)web_view; (void)result;
1360 if (argv_idx(argv, 0))
1361 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1362 TRUE, &uzbl.comm.sync_stdout);
1366 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1367 (void)web_view; (void)result;
1368 if (!uzbl.behave.shell_cmd) {
1369 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1374 gchar *spacer = g_strdup("");
1375 g_array_insert_val(argv, 1, spacer);
1376 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1378 for (i = 1; i < g_strv_length(cmd); i++)
1379 g_array_prepend_val(argv, cmd[i]);
1381 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1387 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1388 (void)web_view; (void)result;
1389 if (!uzbl.behave.shell_cmd) {
1390 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1395 gchar *spacer = g_strdup("");
1396 g_array_insert_val(argv, 1, spacer);
1397 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1399 for (i = 1; i < g_strv_length(cmd); i++)
1400 g_array_prepend_val(argv, cmd[i]);
1402 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1403 TRUE, &uzbl.comm.sync_stdout);
1409 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1410 (void)web_view; (void)result;
1413 struct sockaddr_un sa;
1420 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1422 /* This function could be optimised by storing a hash table of socket paths
1423 and associated connected file descriptors rather than closing and
1424 re-opening for every call. Also we could launch a script if socket connect
1427 /* First element argv[0] is path to socket. Following elements are tokens to
1428 write to the socket. We write them as a single packet with each token
1429 separated by an ASCII nul (\0). */
1431 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1436 /* copy socket path, null terminate result */
1437 sockpath = g_array_index(argv, char*, 0);
1438 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1439 sa.sun_family = AF_UNIX;
1441 /* create socket file descriptor and connect it to path */
1442 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1444 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1447 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1448 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1453 /* build request vector */
1454 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1456 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1460 for(i = 1; i < argv->len; ++i) {
1461 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1462 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1466 ret = writev(fd, iov, argv->len - 1);
1469 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1474 /* wait for a response, with a 500ms timeout */
1476 pfd.events = POLLIN;
1478 ret = poll(&pfd, 1, 500);
1480 if(ret == 0) errno = ETIMEDOUT;
1481 if(errno == EINTR) continue;
1482 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1488 /* get length of response */
1489 if(ioctl(fd, FIONREAD, &len) == -1) {
1490 g_printerr("talk_to_socket: cannot find daemon response length, "
1491 "ioctl failed (%s)\n", strerror(errno));
1496 /* if there is a response, read it */
1498 uzbl.comm.sync_stdout = g_malloc(len + 1);
1499 if(!uzbl.comm.sync_stdout) {
1500 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1504 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1506 ret = read(fd, uzbl.comm.sync_stdout, len);
1508 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1521 parse_command(const char *cmd, const char *param, GString *result) {
1524 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1526 gchar **par = split_quoted(param, TRUE);
1527 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1529 if (c->no_split) { /* don't split */
1530 sharg_append(a, param);
1532 for (i = 0; i < g_strv_length(par); i++)
1533 sharg_append(a, par[i]);
1536 if (result == NULL) {
1537 GString *result_print = g_string_new("");
1539 c->function(uzbl.gui.web_view, a, result_print);
1540 if (result_print->len)
1541 printf("%*s\n", (int)result_print->len, result_print->str);
1543 g_string_free(result_print, TRUE);
1545 c->function(uzbl.gui.web_view, a, result);
1548 g_array_free (a, TRUE);
1551 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1558 if(*uzbl.net.proxy_url == ' '
1559 || uzbl.net.proxy_url == NULL) {
1560 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1561 (GType) SOUP_SESSION_PROXY_URI);
1564 suri = soup_uri_new(uzbl.net.proxy_url);
1565 g_object_set(G_OBJECT(uzbl.net.soup_session),
1566 SOUP_SESSION_PROXY_URI,
1568 soup_uri_free(suri);
1575 if(file_exists(uzbl.gui.icon)) {
1576 if (uzbl.gui.main_window)
1577 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1579 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1585 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1586 g_array_append_val (a, uzbl.state.uri);
1587 load_uri(uzbl.gui.web_view, a, NULL);
1588 g_array_free (a, TRUE);
1592 cmd_always_insert_mode() {
1593 set_insert_mode(uzbl.behave.always_insert_mode);
1599 g_object_set(G_OBJECT(uzbl.net.soup_session),
1600 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1604 cmd_max_conns_host() {
1605 g_object_set(G_OBJECT(uzbl.net.soup_session),
1606 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1611 soup_session_remove_feature
1612 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1613 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1614 /*g_free(uzbl.net.soup_logger);*/
1616 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1617 soup_session_add_feature(uzbl.net.soup_session,
1618 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1623 return webkit_web_view_get_settings(uzbl.gui.web_view);
1628 WebKitWebSettings *ws = view_settings();
1629 if (uzbl.behave.font_size > 0) {
1630 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1633 if (uzbl.behave.monospace_size > 0) {
1634 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1635 uzbl.behave.monospace_size, NULL);
1637 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1638 uzbl.behave.font_size, NULL);
1643 cmd_default_font_family() {
1644 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1645 uzbl.behave.default_font_family, NULL);
1649 cmd_monospace_font_family() {
1650 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1651 uzbl.behave.monospace_font_family, NULL);
1655 cmd_sans_serif_font_family() {
1656 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1657 uzbl.behave.sans_serif_font_family, NULL);
1661 cmd_serif_font_family() {
1662 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1663 uzbl.behave.serif_font_family, NULL);
1667 cmd_cursive_font_family() {
1668 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1669 uzbl.behave.cursive_font_family, NULL);
1673 cmd_fantasy_font_family() {
1674 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1675 uzbl.behave.fantasy_font_family, NULL);
1680 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1684 cmd_disable_plugins() {
1685 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1686 !uzbl.behave.disable_plugins, NULL);
1690 cmd_disable_scripts() {
1691 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1692 !uzbl.behave.disable_scripts, NULL);
1696 cmd_minimum_font_size() {
1697 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1698 uzbl.behave.minimum_font_size, NULL);
1701 cmd_autoload_img() {
1702 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1703 uzbl.behave.autoload_img, NULL);
1708 cmd_autoshrink_img() {
1709 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1710 uzbl.behave.autoshrink_img, NULL);
1715 cmd_enable_spellcheck() {
1716 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1717 uzbl.behave.enable_spellcheck, NULL);
1721 cmd_enable_private() {
1722 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1723 uzbl.behave.enable_private, NULL);
1728 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1729 uzbl.behave.print_bg, NULL);
1734 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1735 uzbl.behave.style_uri, NULL);
1739 cmd_resizable_txt() {
1740 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1741 uzbl.behave.resizable_txt, NULL);
1745 cmd_default_encoding() {
1746 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1747 uzbl.behave.default_encoding, NULL);
1751 cmd_enforce_96dpi() {
1752 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1753 uzbl.behave.enforce_96dpi, NULL);
1757 cmd_caret_browsing() {
1758 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1759 uzbl.behave.caret_browsing, NULL);
1763 cmd_cookie_handler() {
1764 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1765 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1766 if ((g_strcmp0(split[0], "sh") == 0) ||
1767 (g_strcmp0(split[0], "spawn") == 0)) {
1768 g_free (uzbl.behave.cookie_handler);
1769 uzbl.behave.cookie_handler =
1770 g_strdup_printf("sync_%s %s", split[0], split[1]);
1777 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1778 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1779 if ((g_strcmp0(split[0], "sh") == 0) ||
1780 (g_strcmp0(split[0], "spawn") == 0)) {
1781 g_free (uzbl.behave.new_window);
1782 uzbl.behave.new_window =
1783 g_strdup_printf("%s %s", split[0], split[1]);
1790 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1795 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1800 if(uzbl.behave.inject_html) {
1801 webkit_web_view_load_html_string (uzbl.gui.web_view,
1802 uzbl.behave.inject_html, NULL);
1811 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1812 uzbl.behave.modmask = 0;
1814 if(uzbl.behave.modkey)
1815 g_free(uzbl.behave.modkey);
1816 uzbl.behave.modkey = buf;
1818 for (i = 0; modkeys[i].key != NULL; i++) {
1819 if (g_strrstr(buf, modkeys[i].key))
1820 uzbl.behave.modmask |= modkeys[i].mask;
1826 if (*uzbl.net.useragent == ' ') {
1827 g_free (uzbl.net.useragent);
1828 uzbl.net.useragent = NULL;
1830 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1831 uzbl.net.useragent, NULL);
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(gchar *name, gchar *val) {
1858 uzbl_cmdprop *c = NULL;
1862 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1863 if(!c->writeable) return FALSE;
1865 /* check for the variable type */
1866 if (c->type == TYPE_STR) {
1867 buf = expand(val, 0);
1870 } else if(c->type == TYPE_INT) {
1871 buf = expand(val, 0);
1872 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1874 } else if (c->type == TYPE_FLOAT) {
1875 buf = expand(val, 0);
1876 *c->ptr.f = 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 retreive_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.s ? *c->ptr.s : " ");
2742 else if(c->type == TYPE_INT)
2743 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2744 else if(c->type == TYPE_FLOAT)
2745 printf("set %s = %f\n", (char *)k, *c->ptr.f);
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 retreive_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 if(uzbl.gui.geometry)
2897 retreive_geometry();
2899 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2900 if (argc > 1 && !uzbl.state.uri)
2901 uri_override = g_strdup(argv[1]);
2902 gboolean verbose_override = uzbl.state.verbose;
2905 set_insert_mode(FALSE);
2907 if (!uzbl.behave.show_status)
2908 gtk_widget_hide(uzbl.gui.mainbar);
2915 if (verbose_override > uzbl.state.verbose)
2916 uzbl.state.verbose = verbose_override;
2919 set_var_value("uri", uri_override);
2920 g_free(uri_override);
2921 } else if (uzbl.state.uri)
2927 return EXIT_SUCCESS;
2931 /* vi: set et ts=4: */