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>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
77 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
78 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
79 "Print the version and exit", NULL },
80 { NULL, 0, 0, 0, NULL, NULL, NULL }
83 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
85 /* associate command names to their properties */
86 typedef const struct {
87 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
88 the PTR() macro is kind of preventing this change at the moment. */
93 /*@null@*/ void (*func)(void);
96 /* abbreviations to help keep the table's width humane */
97 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
98 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
103 } var_name_to_ptr[] = {
104 /* variable name pointer to variable in code type dump callback function */
105 /* ---------------------------------------------------------------------------------------------- */
106 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
107 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
108 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
109 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
110 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
111 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
112 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
113 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
114 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
115 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
116 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
117 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
118 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
119 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
120 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
121 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
122 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
124 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
125 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
126 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
127 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
128 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
129 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
130 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
131 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
132 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
133 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
134 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
135 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
136 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
137 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
138 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
139 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
140 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
141 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
142 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
143 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
144 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
145 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
147 /* exported WebKitWebSettings properties */
148 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
149 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
150 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
151 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
152 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
153 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
154 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
155 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
156 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
157 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
158 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
159 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
160 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
161 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
162 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
163 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
165 /* constants (not dumpable or writeable) */
166 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
167 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
168 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
169 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
170 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
171 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
172 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
173 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
174 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
175 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
176 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
178 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
179 }, *n2v_p = var_name_to_ptr;
183 /*@null@*/ char *key;
186 { "SHIFT", GDK_SHIFT_MASK }, // shift
187 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
188 { "CONTROL", GDK_CONTROL_MASK }, // control
189 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
190 { "MOD2", GDK_MOD2_MASK }, // 5th mod
191 { "MOD3", GDK_MOD3_MASK }, // 6th mod
192 { "MOD4", GDK_MOD4_MASK }, // 7th mod
193 { "MOD5", GDK_MOD5_MASK }, // 8th mod
194 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
195 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
196 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
197 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
198 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
199 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
200 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
201 { "META", GDK_META_MASK }, // meta (since 2.10)
206 /* construct a hash from the var_name_to_ptr array for quick access */
208 make_var_to_name_hash() {
209 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
211 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
216 /* --- UTILITY FUNCTIONS --- */
217 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
219 get_exp_type(const gchar *s) {
223 else if(*(s+1) == '{')
224 return EXP_BRACED_VAR;
225 else if(*(s+1) == '<')
227 else if(*(s+1) == '[')
230 return EXP_SIMPLE_VAR;
237 * recurse == 1: don't expand '@(command)@'
238 * recurse == 2: don't expand '@<java script>@'
241 expand(const char *s, guint recurse) {
244 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
248 gchar *cmd_stdout = NULL;
250 GString *buf = g_string_new("");
251 GString *js_ret = g_string_new("");
256 g_string_append_c(buf, *++s);
261 etype = get_exp_type(s);
266 vend = strpbrk(s, end_simple_var);
267 if(!vend) vend = strchr(s, '\0');
271 vend = strchr(s, '}');
272 if(!vend) vend = strchr(s, '\0');
276 vend = strstr(s, ")@");
277 if(!vend) vend = strchr(s, '\0');
281 vend = strstr(s, ">@");
282 if(!vend) vend = strchr(s, '\0');
286 vend = strstr(s, "]@");
287 if(!vend) vend = strchr(s, '\0');
292 ret = g_strndup(s, vend-s);
294 if(etype == EXP_SIMPLE_VAR ||
295 etype == EXP_BRACED_VAR) {
296 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
297 if(c->type == TYPE_STR && *c->ptr != NULL) {
298 g_string_append(buf, (gchar *)*c->ptr);
299 } else if(c->type == TYPE_INT) {
300 g_string_append_printf(buf, "%d", (int)*c->ptr);
302 else if(c->type == TYPE_FLOAT) {
303 g_string_append_printf(buf, "%f", *(float *)c->ptr);
307 if(etype == EXP_SIMPLE_VAR)
312 else if(recurse != 1 &&
314 mycmd = expand(ret, 1);
315 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
319 g_printerr("error on running command: %s\n", err->message);
322 else if (*cmd_stdout) {
323 size_t len = strlen(cmd_stdout);
325 if(len > 0 && cmd_stdout[len-1] == '\n')
326 cmd_stdout[--len] = '\0'; /* strip trailing newline */
328 g_string_append(buf, cmd_stdout);
333 else if(recurse != 2 &&
335 mycmd = expand(ret, 2);
336 eval_js(uzbl.gui.web_view, mycmd, js_ret);
340 g_string_append(buf, js_ret->str);
341 g_string_free(js_ret, TRUE);
342 js_ret = g_string_new("");
346 else if(etype == EXP_ESCAPE) {
347 mycmd = expand(ret, 0);
348 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
350 g_string_append(buf, escaped);
362 g_string_append_c(buf, *s);
367 g_string_free(js_ret, TRUE);
368 return g_string_free(buf, FALSE);
375 snprintf(tmp, sizeof(tmp), "%i", val);
376 return g_strdup(tmp);
380 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
383 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
386 str_replace (const char* search, const char* replace, const char* string) {
390 buf = g_strsplit (string, search, -1);
391 ret = g_strjoinv (replace, buf);
392 g_strfreev(buf); // somebody said this segfaults
398 read_file_by_line (gchar *path) {
399 GIOChannel *chan = NULL;
400 gchar *readbuf = NULL;
402 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
405 chan = g_io_channel_new_file(path, "r", NULL);
408 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
409 const gchar* val = g_strdup (readbuf);
410 g_array_append_val (lines, val);
415 g_io_channel_unref (chan);
417 fprintf(stderr, "File '%s' not be read.\n", path);
424 parseenv (char* string) {
425 extern char** environ;
426 gchar* tmpstr = NULL;
430 while (environ[i] != NULL) {
431 gchar** env = g_strsplit (environ[i], "=", 2);
432 gchar* envname = g_strconcat ("$", env[0], NULL);
434 if (g_strrstr (string, envname) != NULL) {
435 tmpstr = g_strdup(string);
437 string = str_replace(envname, env[1], tmpstr);
442 g_strfreev (env); // somebody said this breaks uzbl
450 setup_signal(int signr, sigfunc *shandler) {
451 struct sigaction nh, oh;
453 nh.sa_handler = shandler;
454 sigemptyset(&nh.sa_mask);
457 if(sigaction(signr, &nh, &oh) < 0)
465 if (uzbl.behave.fifo_dir)
466 unlink (uzbl.comm.fifo_path);
467 if (uzbl.behave.socket_dir)
468 unlink (uzbl.comm.socket_path);
470 g_free(uzbl.state.executable_path);
471 g_free(uzbl.state.keycmd);
472 g_hash_table_destroy(uzbl.bindings);
473 g_hash_table_destroy(uzbl.behave.commands);
476 /* used for html_mode_timeout
477 * be sure to extend this function to use
478 * more timers if needed in other places
481 set_timeout(int seconds) {
483 memset(&t, 0, sizeof t);
485 t.it_value.tv_sec = seconds;
486 t.it_value.tv_usec = 0;
487 setitimer(ITIMER_REAL, &t, NULL);
490 /* --- SIGNAL HANDLER --- */
493 catch_sigterm(int s) {
499 catch_sigint(int s) {
509 set_var_value("mode", "0");
514 /* --- CALLBACKS --- */
517 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
520 (void) navigation_action;
521 (void) policy_decision;
523 const gchar* uri = webkit_network_request_get_uri (request);
524 if (uzbl.state.verbose)
525 printf("New window requested -> %s \n", uri);
526 webkit_web_policy_decision_use(policy_decision);
531 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
536 /* If we can display it, let's display it... */
537 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
538 webkit_web_policy_decision_use (policy_decision);
542 /* ...everything we can't displayed is downloaded */
543 webkit_web_policy_decision_download (policy_decision);
548 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
552 if (uzbl.state.selected_url != NULL) {
553 if (uzbl.state.verbose)
554 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
555 new_window_load_uri(uzbl.state.selected_url);
557 if (uzbl.state.verbose)
558 printf("New web view -> %s\n","Nothing to open, exiting");
564 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
567 if (uzbl.behave.download_handler) {
568 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
569 if (uzbl.state.verbose)
570 printf("Download -> %s\n",uri);
571 /* if urls not escaped, we may have to escape and quote uri before this call */
572 run_handler(uzbl.behave.download_handler, uri);
577 /* scroll a bar in a given direction */
579 scroll (GtkAdjustment* bar, GArray *argv) {
583 gdouble page_size = gtk_adjustment_get_page_size(bar);
584 gdouble value = gtk_adjustment_get_value(bar);
585 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
588 value += page_size * amount * 0.01;
592 max_value = gtk_adjustment_get_upper(bar) - page_size;
594 if (value > max_value)
595 value = max_value; /* don't scroll past the end of the page */
597 gtk_adjustment_set_value (bar, value);
601 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
602 (void) page; (void) argv; (void) result;
603 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
607 scroll_end(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_upper(uzbl.gui.bar_v) -
610 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
614 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
615 (void) page; (void) result;
616 scroll(uzbl.gui.bar_v, argv);
620 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
621 (void) page; (void) result;
622 scroll(uzbl.gui.bar_h, argv);
627 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
628 if(uzbl.state.verbose)
629 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
631 /* update geometry var with the actual geometry
632 this is necessary as some WMs don't seem to honour
633 the above setting and we don't want to end up with
634 wrong geometry information
641 if (!uzbl.behave.show_status) {
642 gtk_widget_hide(uzbl.gui.mainbar);
644 gtk_widget_show(uzbl.gui.mainbar);
650 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
655 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
659 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
664 if (uzbl.behave.show_status) {
665 gtk_widget_hide(uzbl.gui.mainbar);
667 gtk_widget_show(uzbl.gui.mainbar);
669 uzbl.behave.show_status = !uzbl.behave.show_status;
674 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
678 //Set selected_url state variable
679 g_free(uzbl.state.selected_url);
680 uzbl.state.selected_url = NULL;
682 uzbl.state.selected_url = g_strdup(link);
688 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
691 const gchar *title = webkit_web_view_get_title(web_view);
692 if (uzbl.gui.main_title)
693 g_free (uzbl.gui.main_title);
694 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
699 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
702 uzbl.gui.sbar.load_progress = progress;
704 g_free(uzbl.gui.sbar.progress_bar);
705 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
711 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
715 if (uzbl.behave.load_finish_handler)
716 run_handler(uzbl.behave.load_finish_handler, "");
719 void clear_keycmd() {
720 g_free(uzbl.state.keycmd);
721 uzbl.state.keycmd = g_strdup("");
725 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
729 uzbl.gui.sbar.load_progress = 0;
730 clear_keycmd(); // don't need old commands to remain on new page?
731 if (uzbl.behave.load_start_handler)
732 run_handler(uzbl.behave.load_start_handler, "");
736 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
739 g_free (uzbl.state.uri);
740 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
741 uzbl.state.uri = g_string_free (newuri, FALSE);
742 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
743 set_insert_mode(uzbl.behave.always_insert_mode);
746 if (uzbl.behave.load_commit_handler)
747 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
751 destroy_cb (GtkWidget* widget, gpointer data) {
759 if (uzbl.behave.history_handler) {
761 struct tm * timeinfo;
764 timeinfo = localtime ( &rawtime );
765 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
766 run_handler(uzbl.behave.history_handler, date);
771 /* VIEW funcs (little webkit wrappers) */
772 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
774 VIEWFUNC(reload_bypass_cache)
775 VIEWFUNC(stop_loading)
782 /* -- command to callback/function map for things we cannot attach to any signals */
783 struct {char *key; CommandInfo value;} cmdlist[] =
784 { /* key function no_split */
785 { "back", {view_go_back, 0} },
786 { "forward", {view_go_forward, 0} },
787 { "scroll_vert", {scroll_vert, 0} },
788 { "scroll_horz", {scroll_horz, 0} },
789 { "scroll_begin", {scroll_begin, 0} },
790 { "scroll_end", {scroll_end, 0} },
791 { "reload", {view_reload, 0}, },
792 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
793 { "stop", {view_stop_loading, 0}, },
794 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
795 { "zoom_out", {view_zoom_out, 0}, },
796 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
797 { "uri", {load_uri, TRUE} },
798 { "js", {run_js, TRUE} },
799 { "script", {run_external_js, 0} },
800 { "toggle_status", {toggle_status_cb, 0} },
801 { "spawn", {spawn, 0} },
802 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
803 { "sh", {spawn_sh, 0} },
804 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
805 { "exit", {close_uzbl, 0} },
806 { "search", {search_forward_text, TRUE} },
807 { "search_reverse", {search_reverse_text, TRUE} },
808 { "dehilight", {dehilight, 0} },
809 { "toggle_insert_mode", {toggle_insert_mode, 0} },
810 { "set", {set_var, TRUE} },
811 //{ "get", {get_var, TRUE} },
812 { "bind", {act_bind, TRUE} },
813 { "dump_config", {act_dump_config, 0} },
814 { "keycmd", {keycmd, TRUE} },
815 { "keycmd_nl", {keycmd_nl, TRUE} },
816 { "keycmd_bs", {keycmd_bs, 0} },
817 { "chain", {chain, 0} },
818 { "print", {print, TRUE} }
825 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
827 for (i = 0; i < LENGTH(cmdlist); i++)
828 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
831 /* -- CORE FUNCTIONS -- */
834 free_action(gpointer act) {
835 Action *action = (Action*)act;
836 g_free(action->name);
838 g_free(action->param);
843 new_action(const gchar *name, const gchar *param) {
844 Action *action = g_new(Action, 1);
846 action->name = g_strdup(name);
848 action->param = g_strdup(param);
850 action->param = NULL;
856 file_exists (const char * filename) {
857 return (access(filename, F_OK) == 0);
861 set_var(WebKitWebView *page, GArray *argv, GString *result) {
862 (void) page; (void) result;
863 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
864 if (split[0] != NULL) {
865 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
866 set_var_value(g_strstrip(split[0]), value);
873 print(WebKitWebView *page, GArray *argv, GString *result) {
874 (void) page; (void) result;
877 buf = expand(argv_idx(argv, 0), 0);
878 g_string_assign(result, buf);
883 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
884 (void) page; (void) result;
885 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
886 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
887 add_binding(g_strstrip(split[0]), value);
905 set_mode_indicator() {
906 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
907 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
912 set_mode_indicator();
917 set_insert_mode(gboolean mode) {
918 uzbl.behave.insert_mode = mode;
919 set_mode_indicator();
923 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
924 (void) page; (void) result;
926 if (argv_idx(argv, 0)) {
927 if (strcmp (argv_idx(argv, 0), "0") == 0) {
928 set_insert_mode(FALSE);
930 set_insert_mode(TRUE);
933 set_insert_mode( !uzbl.behave.insert_mode );
940 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
943 if (argv_idx(argv, 0)) {
944 GString* newuri = g_string_new (argv_idx(argv, 0));
945 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
946 run_js(web_view, argv, NULL);
949 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
950 g_string_prepend (newuri, "http://");
951 /* if we do handle cookies, ask our handler for them */
952 webkit_web_view_load_uri (web_view, newuri->str);
953 g_string_free (newuri, TRUE);
960 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
961 size_t argumentCount, const JSValueRef arguments[],
962 JSValueRef* exception) {
967 JSStringRef js_result_string;
968 GString *result = g_string_new("");
970 if (argumentCount >= 1) {
971 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
972 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
973 char ctl_line[arg_size];
974 JSStringGetUTF8CString(arg, ctl_line, arg_size);
976 parse_cmd_line(ctl_line, result);
978 JSStringRelease(arg);
980 js_result_string = JSStringCreateWithUTF8CString(result->str);
982 g_string_free(result, TRUE);
984 return JSValueMakeString(ctx, js_result_string);
987 JSStaticFunction js_static_functions[] = {
988 {"run", js_run_command, kJSPropertyAttributeNone},
993 /* This function creates the class and its definition, only once */
994 if (!uzbl.js.initialized) {
995 /* it would be pretty cool to make this dynamic */
996 uzbl.js.classdef = kJSClassDefinitionEmpty;
997 uzbl.js.classdef.staticFunctions = js_static_functions;
999 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1005 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1006 WebKitWebFrame *frame;
1007 JSGlobalContextRef context;
1008 JSObjectRef globalobject;
1009 JSStringRef var_name;
1011 JSStringRef js_script;
1012 JSValueRef js_result;
1013 JSStringRef js_result_string;
1014 size_t js_result_size;
1018 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1019 context = webkit_web_frame_get_global_context(frame);
1020 globalobject = JSContextGetGlobalObject(context);
1022 /* uzbl javascript namespace */
1023 var_name = JSStringCreateWithUTF8CString("Uzbl");
1024 JSObjectSetProperty(context, globalobject, var_name,
1025 JSObjectMake(context, uzbl.js.classref, NULL),
1026 kJSClassAttributeNone, NULL);
1028 /* evaluate the script and get return value*/
1029 js_script = JSStringCreateWithUTF8CString(script);
1030 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1031 if (js_result && !JSValueIsUndefined(context, js_result)) {
1032 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1033 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1035 if (js_result_size) {
1036 char js_result_utf8[js_result_size];
1037 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1038 g_string_assign(result, js_result_utf8);
1041 JSStringRelease(js_result_string);
1045 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1047 JSStringRelease(var_name);
1048 JSStringRelease(js_script);
1052 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1053 if (argv_idx(argv, 0))
1054 eval_js(web_view, argv_idx(argv, 0), result);
1058 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1060 if (argv_idx(argv, 0)) {
1061 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1066 while ((line = g_array_index(lines, gchar*, i))) {
1068 js = g_strdup (line);
1070 gchar* newjs = g_strconcat (js, line, NULL);
1077 if (uzbl.state.verbose)
1078 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1080 if (argv_idx (argv, 1)) {
1081 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1085 eval_js (web_view, js, result);
1087 g_array_free (lines, TRUE);
1092 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1093 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1094 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1095 webkit_web_view_unmark_text_matches (page);
1096 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1097 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1101 if (uzbl.state.searchtx) {
1102 if (uzbl.state.verbose)
1103 printf ("Searching: %s\n", uzbl.state.searchtx);
1104 webkit_web_view_set_highlight_text_matches (page, TRUE);
1105 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1110 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1112 search_text(page, argv, TRUE);
1116 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1118 search_text(page, argv, FALSE);
1122 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1123 (void) argv; (void) result;
1124 webkit_web_view_set_highlight_text_matches (page, FALSE);
1129 new_window_load_uri (const gchar * uri) {
1130 if (uzbl.behave.new_window) {
1131 GString *s = g_string_new ("");
1132 g_string_printf(s, "'%s'", uri);
1133 run_handler(uzbl.behave.new_window, s->str);
1136 GString* to_execute = g_string_new ("");
1137 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1139 for (i = 0; entries[i].long_name != NULL; i++) {
1140 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1141 gchar** str = (gchar**)entries[i].arg_data;
1143 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1147 if (uzbl.state.verbose)
1148 printf("\n%s\n", to_execute->str);
1149 g_spawn_command_line_async (to_execute->str, NULL);
1150 g_string_free (to_execute, TRUE);
1154 chain (WebKitWebView *page, GArray *argv, GString *result) {
1155 (void) page; (void) result;
1157 gchar **parts = NULL;
1159 while ((a = argv_idx(argv, i++))) {
1160 parts = g_strsplit (a, " ", 2);
1162 parse_command(parts[0], parts[1], result);
1168 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1172 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1178 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1182 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1188 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1193 int len = strlen(uzbl.state.keycmd);
1194 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1196 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1201 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1208 /* --Statusbar functions-- */
1210 build_progressbar_ascii(int percent) {
1211 int width=uzbl.gui.sbar.progress_w;
1214 GString *bar = g_string_new("");
1216 l = (double)percent*((double)width/100.);
1217 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1219 for(i=0; i<(int)l; i++)
1220 g_string_append(bar, uzbl.gui.sbar.progress_s);
1223 g_string_append(bar, uzbl.gui.sbar.progress_u);
1225 return g_string_free(bar, FALSE);
1227 /* --End Statusbar functions-- */
1230 sharg_append(GArray *a, const gchar *str) {
1231 const gchar *s = (str ? str : "");
1232 g_array_append_val(a, s);
1235 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1237 run_command (const gchar *command, const guint npre, const gchar **args,
1238 const gboolean sync, char **output_stdout) {
1239 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1242 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1243 gchar *pid = itos(getpid());
1244 gchar *xwin = itos(uzbl.xwin);
1246 sharg_append(a, command);
1247 for (i = 0; i < npre; i++) /* add n args before the default vars */
1248 sharg_append(a, args[i]);
1249 sharg_append(a, uzbl.state.config_file);
1250 sharg_append(a, pid);
1251 sharg_append(a, xwin);
1252 sharg_append(a, uzbl.comm.fifo_path);
1253 sharg_append(a, uzbl.comm.socket_path);
1254 sharg_append(a, uzbl.state.uri);
1255 sharg_append(a, uzbl.gui.main_title);
1257 for (i = npre; i < g_strv_length((gchar**)args); i++)
1258 sharg_append(a, args[i]);
1262 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1264 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1265 NULL, NULL, output_stdout, NULL, NULL, &err);
1266 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1267 NULL, NULL, NULL, &err);
1269 if (uzbl.state.verbose) {
1270 GString *s = g_string_new("spawned:");
1271 for (i = 0; i < (a->len); i++) {
1272 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1273 g_string_append_printf(s, " %s", qarg);
1276 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1277 printf("%s\n", s->str);
1278 g_string_free(s, TRUE);
1280 printf("Stdout: %s\n", *output_stdout);
1284 g_printerr("error on run_command: %s\n", err->message);
1289 g_array_free (a, TRUE);
1294 split_quoted(const gchar* src, const gboolean unquote) {
1295 /* split on unquoted space, return array of strings;
1296 remove a layer of quotes and backslashes if unquote */
1297 if (!src) return NULL;
1299 gboolean dq = FALSE;
1300 gboolean sq = FALSE;
1301 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1302 GString *s = g_string_new ("");
1306 for (p = src; *p != '\0'; p++) {
1307 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1308 else if (*p == '\\') { g_string_append_c(s, *p++);
1309 g_string_append_c(s, *p); }
1310 else if ((*p == '"') && unquote && !sq) dq = !dq;
1311 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1313 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1314 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1316 else if ((*p == ' ') && !dq && !sq) {
1317 dup = g_strdup(s->str);
1318 g_array_append_val(a, dup);
1319 g_string_truncate(s, 0);
1320 } else g_string_append_c(s, *p);
1322 dup = g_strdup(s->str);
1323 g_array_append_val(a, dup);
1324 ret = (gchar**)a->data;
1325 g_array_free (a, FALSE);
1326 g_string_free (s, TRUE);
1331 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1332 (void)web_view; (void)result;
1333 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1334 if (argv_idx(argv, 0))
1335 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1339 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1340 (void)web_view; (void)result;
1342 if (argv_idx(argv, 0))
1343 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1344 TRUE, &uzbl.comm.sync_stdout);
1348 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1349 (void)web_view; (void)result;
1350 if (!uzbl.behave.shell_cmd) {
1351 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1356 gchar *spacer = g_strdup("");
1357 g_array_insert_val(argv, 1, spacer);
1358 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1360 for (i = 1; i < g_strv_length(cmd); i++)
1361 g_array_prepend_val(argv, cmd[i]);
1363 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1369 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1370 (void)web_view; (void)result;
1371 if (!uzbl.behave.shell_cmd) {
1372 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1377 gchar *spacer = g_strdup("");
1378 g_array_insert_val(argv, 1, spacer);
1379 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1381 for (i = 1; i < g_strv_length(cmd); i++)
1382 g_array_prepend_val(argv, cmd[i]);
1384 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1385 TRUE, &uzbl.comm.sync_stdout);
1391 parse_command(const char *cmd, const char *param, GString *result) {
1394 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1396 gchar **par = split_quoted(param, TRUE);
1397 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1399 if (c->no_split) { /* don't split */
1400 sharg_append(a, param);
1402 for (i = 0; i < g_strv_length(par); i++)
1403 sharg_append(a, par[i]);
1406 if (result == NULL) {
1407 GString *result_print = g_string_new("");
1409 c->function(uzbl.gui.web_view, a, result_print);
1410 if (result_print->len)
1411 printf("%*s\n", result_print->len, result_print->str);
1413 g_string_free(result_print, TRUE);
1415 c->function(uzbl.gui.web_view, a, result);
1418 g_array_free (a, TRUE);
1421 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1428 if(*uzbl.net.proxy_url == ' '
1429 || uzbl.net.proxy_url == NULL) {
1430 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1431 (GType) SOUP_SESSION_PROXY_URI);
1434 suri = soup_uri_new(uzbl.net.proxy_url);
1435 g_object_set(G_OBJECT(uzbl.net.soup_session),
1436 SOUP_SESSION_PROXY_URI,
1438 soup_uri_free(suri);
1445 if(file_exists(uzbl.gui.icon)) {
1446 if (uzbl.gui.main_window)
1447 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1449 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1455 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1456 g_array_append_val (a, uzbl.state.uri);
1457 load_uri(uzbl.gui.web_view, a, NULL);
1458 g_array_free (a, TRUE);
1462 cmd_always_insert_mode() {
1463 set_insert_mode(uzbl.behave.always_insert_mode);
1469 g_object_set(G_OBJECT(uzbl.net.soup_session),
1470 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1474 cmd_max_conns_host() {
1475 g_object_set(G_OBJECT(uzbl.net.soup_session),
1476 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1481 soup_session_remove_feature
1482 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1483 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1484 /*g_free(uzbl.net.soup_logger);*/
1486 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1487 soup_session_add_feature(uzbl.net.soup_session,
1488 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1493 return webkit_web_view_get_settings(uzbl.gui.web_view);
1498 WebKitWebSettings *ws = view_settings();
1499 if (uzbl.behave.font_size > 0) {
1500 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1503 if (uzbl.behave.monospace_size > 0) {
1504 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1505 uzbl.behave.monospace_size, NULL);
1507 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1508 uzbl.behave.font_size, NULL);
1514 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1518 cmd_disable_plugins() {
1519 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1520 !uzbl.behave.disable_plugins, NULL);
1524 cmd_disable_scripts() {
1525 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1526 !uzbl.behave.disable_scripts, NULL);
1530 cmd_minimum_font_size() {
1531 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1532 uzbl.behave.minimum_font_size, NULL);
1535 cmd_autoload_img() {
1536 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1537 uzbl.behave.autoload_img, NULL);
1542 cmd_autoshrink_img() {
1543 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1544 uzbl.behave.autoshrink_img, NULL);
1549 cmd_enable_spellcheck() {
1550 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1551 uzbl.behave.enable_spellcheck, NULL);
1555 cmd_enable_private() {
1556 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1557 uzbl.behave.enable_private, NULL);
1562 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1563 uzbl.behave.print_bg, NULL);
1568 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1569 uzbl.behave.style_uri, NULL);
1573 cmd_resizable_txt() {
1574 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1575 uzbl.behave.resizable_txt, NULL);
1579 cmd_default_encoding() {
1580 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1581 uzbl.behave.default_encoding, NULL);
1585 cmd_enforce_96dpi() {
1586 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1587 uzbl.behave.enforce_96dpi, NULL);
1591 cmd_caret_browsing() {
1592 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1593 uzbl.behave.caret_browsing, NULL);
1597 cmd_cookie_handler() {
1598 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1599 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1600 if ((g_strcmp0(split[0], "sh") == 0) ||
1601 (g_strcmp0(split[0], "spawn") == 0)) {
1602 g_free (uzbl.behave.cookie_handler);
1603 uzbl.behave.cookie_handler =
1604 g_strdup_printf("sync_%s %s", split[0], split[1]);
1611 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1612 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1613 if ((g_strcmp0(split[0], "sh") == 0) ||
1614 (g_strcmp0(split[0], "spawn") == 0)) {
1615 g_free (uzbl.behave.new_window);
1616 uzbl.behave.new_window =
1617 g_strdup_printf("%s %s", split[0], split[1]);
1624 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1629 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1634 if(uzbl.behave.inject_html) {
1635 webkit_web_view_load_html_string (uzbl.gui.web_view,
1636 uzbl.behave.inject_html, NULL);
1645 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1646 uzbl.behave.modmask = 0;
1648 if(uzbl.behave.modkey)
1649 g_free(uzbl.behave.modkey);
1650 uzbl.behave.modkey = buf;
1652 for (i = 0; modkeys[i].key != NULL; i++) {
1653 if (g_strrstr(buf, modkeys[i].key))
1654 uzbl.behave.modmask |= modkeys[i].mask;
1660 if (*uzbl.net.useragent == ' ') {
1661 g_free (uzbl.net.useragent);
1662 uzbl.net.useragent = NULL;
1664 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1665 uzbl.net.useragent, NULL);
1671 gtk_widget_ref(uzbl.gui.scrolled_win);
1672 gtk_widget_ref(uzbl.gui.mainbar);
1673 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1674 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1676 if(uzbl.behave.status_top) {
1677 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1678 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1681 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1682 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1684 gtk_widget_unref(uzbl.gui.scrolled_win);
1685 gtk_widget_unref(uzbl.gui.mainbar);
1686 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1691 set_var_value(gchar *name, gchar *val) {
1692 uzbl_cmdprop *c = NULL;
1696 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1697 if(!c->writeable) return FALSE;
1699 /* check for the variable type */
1700 if (c->type == TYPE_STR) {
1701 buf = expand(val, 0);
1704 } else if(c->type == TYPE_INT) {
1705 int *ip = (int *)c->ptr;
1706 buf = expand(val, 0);
1707 *ip = (int)strtoul(buf, &endp, 10);
1709 } else if (c->type == TYPE_FLOAT) {
1710 float *fp = (float *)c->ptr;
1711 buf = expand(val, 0);
1712 *fp = strtod(buf, &endp);
1716 /* invoke a command specific function */
1717 if(c->func) c->func();
1724 Behaviour *b = &uzbl.behave;
1726 if(b->html_buffer->str) {
1727 webkit_web_view_load_html_string (uzbl.gui.web_view,
1728 b->html_buffer->str, b->base_url);
1729 g_string_free(b->html_buffer, TRUE);
1730 b->html_buffer = g_string_new("");
1734 enum {M_CMD, M_HTML};
1736 parse_cmd_line(const char *ctl_line, GString *result) {
1737 Behaviour *b = &uzbl.behave;
1740 if(b->mode == M_HTML) {
1741 len = strlen(b->html_endmarker);
1742 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1743 if(len == strlen(ctl_line)-1 &&
1744 !strncmp(b->html_endmarker, ctl_line, len)) {
1746 set_var_value("mode", "0");
1751 set_timeout(b->html_timeout);
1752 g_string_append(b->html_buffer, ctl_line);
1755 else if((ctl_line[0] == '#') /* Comments */
1756 || (ctl_line[0] == ' ')
1757 || (ctl_line[0] == '\n'))
1758 ; /* ignore these lines */
1759 else { /* parse a command */
1761 gchar **tokens = NULL;
1762 len = strlen(ctl_line);
1764 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1765 ctlstrip = g_strndup(ctl_line, len - 1);
1766 else ctlstrip = g_strdup(ctl_line);
1768 tokens = g_strsplit(ctlstrip, " ", 2);
1769 parse_command(tokens[0], tokens[1], result);
1776 build_stream_name(int type, const gchar* dir) {
1777 State *s = &uzbl.state;
1781 str = g_strdup_printf
1782 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1783 } else if (type == SOCKET) {
1784 str = g_strdup_printf
1785 ("%s/uzbl_socket_%s", dir, s->instance_name);
1791 control_fifo(GIOChannel *gio, GIOCondition condition) {
1792 if (uzbl.state.verbose)
1793 printf("triggered\n");
1798 if (condition & G_IO_HUP)
1799 g_error ("Fifo: Read end of pipe died!\n");
1802 g_error ("Fifo: GIOChannel broke\n");
1804 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1805 if (ret == G_IO_STATUS_ERROR) {
1806 g_error ("Fifo: Error reading: %s\n", err->message);
1810 parse_cmd_line(ctl_line, NULL);
1817 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1818 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1819 if (unlink(uzbl.comm.fifo_path) == -1)
1820 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1821 g_free(uzbl.comm.fifo_path);
1822 uzbl.comm.fifo_path = NULL;
1825 GIOChannel *chan = NULL;
1826 GError *error = NULL;
1827 gchar *path = build_stream_name(FIFO, dir);
1829 if (!file_exists(path)) {
1830 if (mkfifo (path, 0666) == 0) {
1831 // 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.
1832 chan = g_io_channel_new_file(path, "r+", &error);
1834 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1835 if (uzbl.state.verbose)
1836 printf ("init_fifo: created successfully as %s\n", path);
1837 uzbl.comm.fifo_path = path;
1839 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1840 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1841 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1842 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1844 /* if we got this far, there was an error; cleanup */
1845 if (error) g_error_free (error);
1852 control_stdin(GIOChannel *gio, GIOCondition condition) {
1854 gchar *ctl_line = NULL;
1857 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1858 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1861 parse_cmd_line(ctl_line, NULL);
1869 GIOChannel *chan = NULL;
1870 GError *error = NULL;
1872 chan = g_io_channel_unix_new(fileno(stdin));
1874 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1875 g_error ("Stdin: could not add watch\n");
1877 if (uzbl.state.verbose)
1878 printf ("Stdin: watch added successfully\n");
1881 g_error ("Stdin: Error while opening: %s\n", error->message);
1883 if (error) g_error_free (error);
1887 control_socket(GIOChannel *chan) {
1888 struct sockaddr_un remote;
1889 unsigned int t = sizeof(remote);
1891 GIOChannel *clientchan;
1893 clientsock = accept (g_io_channel_unix_get_fd(chan),
1894 (struct sockaddr *) &remote, &t);
1896 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1897 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1898 (GIOFunc) control_client_socket, clientchan);
1905 control_client_socket(GIOChannel *clientchan) {
1907 GString *result = g_string_new("");
1908 GError *error = NULL;
1912 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1913 if (ret == G_IO_STATUS_ERROR) {
1914 g_warning ("Error reading: %s\n", error->message);
1915 g_io_channel_shutdown(clientchan, TRUE, &error);
1917 } else if (ret == G_IO_STATUS_EOF) {
1918 /* shutdown and remove channel watch from main loop */
1919 g_io_channel_shutdown(clientchan, TRUE, &error);
1924 parse_cmd_line (ctl_line, result);
1925 g_string_append_c(result, '\n');
1926 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1928 if (ret == G_IO_STATUS_ERROR) {
1929 g_warning ("Error writing: %s", error->message);
1931 g_io_channel_flush(clientchan, &error);
1934 if (error) g_error_free (error);
1935 g_string_free(result, TRUE);
1941 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1942 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1943 if (unlink(uzbl.comm.socket_path) == -1)
1944 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1945 g_free(uzbl.comm.socket_path);
1946 uzbl.comm.socket_path = NULL;
1954 GIOChannel *chan = NULL;
1956 struct sockaddr_un local;
1957 gchar *path = build_stream_name(SOCKET, dir);
1959 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1961 local.sun_family = AF_UNIX;
1962 strcpy (local.sun_path, path);
1963 unlink (local.sun_path);
1965 len = strlen (local.sun_path) + sizeof (local.sun_family);
1966 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1967 if (uzbl.state.verbose)
1968 printf ("init_socket: opened in %s\n", path);
1971 if( (chan = g_io_channel_unix_new(sock)) ) {
1972 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1973 uzbl.comm.socket_path = path;
1976 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1978 /* if we got this far, there was an error; cleanup */
1985 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1986 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1988 // this function may be called very early when the templates are not set (yet), hence the checks
1990 update_title (void) {
1991 Behaviour *b = &uzbl.behave;
1994 if (b->show_status) {
1995 if (b->title_format_short) {
1996 parsed = expand(b->title_format_short, 0);
1997 if (uzbl.gui.main_window)
1998 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2001 if (b->status_format) {
2002 parsed = expand(b->status_format, 0);
2003 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2006 if (b->status_background) {
2008 gdk_color_parse (b->status_background, &color);
2009 //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)
2010 if (uzbl.gui.main_window)
2011 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2012 else if (uzbl.gui.plug)
2013 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2016 if (b->title_format_long) {
2017 parsed = expand(b->title_format_long, 0);
2018 if (uzbl.gui.main_window)
2019 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2026 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2030 retreive_geometry();
2035 key_press_cb (GtkWidget* window, GdkEventKey* event)
2037 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2041 if (event->type != GDK_KEY_PRESS ||
2042 event->keyval == GDK_Page_Up ||
2043 event->keyval == GDK_Page_Down ||
2044 event->keyval == GDK_Up ||
2045 event->keyval == GDK_Down ||
2046 event->keyval == GDK_Left ||
2047 event->keyval == GDK_Right ||
2048 event->keyval == GDK_Shift_L ||
2049 event->keyval == GDK_Shift_R)
2052 /* turn off insert mode (if always_insert_mode is not used) */
2053 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2054 set_insert_mode(uzbl.behave.always_insert_mode);
2059 if (uzbl.behave.insert_mode &&
2060 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2061 (!uzbl.behave.modmask)
2066 if (event->keyval == GDK_Escape) {
2069 dehilight(uzbl.gui.web_view, NULL, NULL);
2073 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2074 if (event->keyval == GDK_Insert) {
2076 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2077 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2079 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2082 GString* keycmd = g_string_new(uzbl.state.keycmd);
2083 g_string_append (keycmd, str);
2084 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2091 if (event->keyval == GDK_BackSpace)
2092 keycmd_bs(NULL, NULL, NULL);
2094 gboolean key_ret = FALSE;
2095 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2098 GString* keycmd = g_string_new(uzbl.state.keycmd);
2099 g_string_append(keycmd, event->string);
2100 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2103 run_keycmd(key_ret);
2105 if (key_ret) return (!uzbl.behave.insert_mode);
2110 run_keycmd(const gboolean key_ret) {
2111 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2113 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2115 parse_command(act->name, act->param, NULL);
2119 /* try if it's an incremental keycmd or one that takes args, and run it */
2120 GString* short_keys = g_string_new ("");
2121 GString* short_keys_inc = g_string_new ("");
2123 guint len = strlen(uzbl.state.keycmd);
2124 for (i=0; i<len; i++) {
2125 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2126 g_string_assign(short_keys_inc, short_keys->str);
2127 g_string_append_c(short_keys, '_');
2128 g_string_append_c(short_keys_inc, '*');
2130 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2131 /* run normal cmds only if return was pressed */
2132 exec_paramcmd(act, i);
2135 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2136 if (key_ret) /* just quit the incremental command on return */
2138 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2142 g_string_truncate(short_keys, short_keys->len - 1);
2144 g_string_free (short_keys, TRUE);
2145 g_string_free (short_keys_inc, TRUE);
2149 exec_paramcmd(const Action *act, const guint i) {
2150 GString *parampart = g_string_new (uzbl.state.keycmd);
2151 GString *actionname = g_string_new ("");
2152 GString *actionparam = g_string_new ("");
2153 g_string_erase (parampart, 0, i+1);
2155 g_string_printf (actionname, act->name, parampart->str);
2157 g_string_printf (actionparam, act->param, parampart->str);
2158 parse_command(actionname->str, actionparam->str, NULL);
2159 g_string_free(actionname, TRUE);
2160 g_string_free(actionparam, TRUE);
2161 g_string_free(parampart, TRUE);
2169 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2170 //main_window_ref = g_object_ref(scrolled_window);
2171 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2173 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2174 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2176 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2177 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2178 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2179 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2180 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2181 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2182 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2183 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2184 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2188 return scrolled_window;
2195 g->mainbar = gtk_hbox_new (FALSE, 0);
2197 /* keep a reference to the bar so we can re-pack it at runtime*/
2198 //sbar_ref = g_object_ref(g->mainbar);
2200 g->mainbar_label = gtk_label_new ("");
2201 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2202 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2203 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2204 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2205 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2206 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2212 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2213 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2214 gtk_widget_set_name (window, "Uzbl browser");
2215 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2216 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2217 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2224 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2225 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2226 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2233 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2235 If actname is one that calls an external command, this function will inject
2236 newargs in front of the user-provided args in that command line. They will
2237 come become after the body of the script (in sh) or after the name of
2238 the command to execute (in spawn).
2239 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2240 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2242 The return value consist of two strings: the action (sh, ...) and its args.
2244 If act is not one that calls an external command, then the given action merely
2247 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2248 /* Arrr! Here be memory leaks */
2249 gchar *actdup = g_strdup(actname);
2250 g_array_append_val(rets, actdup);
2252 if ((g_strcmp0(actname, "spawn") == 0) ||
2253 (g_strcmp0(actname, "sh") == 0) ||
2254 (g_strcmp0(actname, "sync_spawn") == 0) ||
2255 (g_strcmp0(actname, "sync_sh") == 0)) {
2257 GString *a = g_string_new("");
2258 gchar **spawnparts = split_quoted(origargs, FALSE);
2259 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2260 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2262 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2263 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2265 g_array_append_val(rets, a->str);
2266 g_string_free(a, FALSE);
2267 g_strfreev(spawnparts);
2269 gchar *origdup = g_strdup(origargs);
2270 g_array_append_val(rets, origdup);
2272 return (gchar**)g_array_free(rets, FALSE);
2276 run_handler (const gchar *act, const gchar *args) {
2277 /* Consider this code a temporary hack to make the handlers usable.
2278 In practice, all this splicing, injection, and reconstruction is
2279 inefficient, annoying and hard to manage. Potential pitfalls arise
2280 when the handler specific args 1) are not quoted (the handler
2281 callbacks should take care of this) 2) are quoted but interfere
2282 with the users' own quotation. A more ideal solution is
2283 to refactor parse_command so that it doesn't just take a string
2284 and execute it; rather than that, we should have a function which
2285 returns the argument vector parsed from the string. This vector
2286 could be modified (e.g. insert additional args into it) before
2287 passing it to the next function that actually executes it. Though
2288 it still isn't perfect for chain actions.. will reconsider & re-
2289 factor when I have the time. -duc */
2291 char **parts = g_strsplit(act, " ", 2);
2293 if (g_strcmp0(parts[0], "chain") == 0) {
2294 GString *newargs = g_string_new("");
2295 gchar **chainparts = split_quoted(parts[1], FALSE);
2297 /* for every argument in the chain, inject the handler args
2298 and make sure the new parts are wrapped in quotes */
2299 gchar **cp = chainparts;
2301 gchar *quotless = NULL;
2302 gchar **spliced_quotless = NULL; // sigh -_-;
2303 gchar **inpart = NULL;
2306 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2308 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2309 } else quotless = g_strdup(*cp);
2311 spliced_quotless = g_strsplit(quotless, " ", 2);
2312 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2313 g_strfreev(spliced_quotless);
2315 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2321 parse_command(parts[0], &(newargs->str[1]), NULL);
2322 g_string_free(newargs, TRUE);
2323 g_strfreev(chainparts);
2326 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2327 parse_command(inparts[0], inparts[1], NULL);
2335 add_binding (const gchar *key, const gchar *act) {
2336 char **parts = g_strsplit(act, " ", 2);
2343 if (uzbl.state.verbose)
2344 printf ("Binding %-10s : %s\n", key, act);
2345 action = new_action(parts[0], parts[1]);
2347 if (g_hash_table_remove (uzbl.bindings, key))
2348 g_warning ("Overwriting existing binding for \"%s\"", key);
2349 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2354 get_xdg_var (XDG_Var xdg) {
2355 const gchar* actual_value = getenv (xdg.environmental);
2356 const gchar* home = getenv ("HOME");
2357 gchar* return_value;
2359 if (! actual_value || strcmp (actual_value, "") == 0) {
2360 if (xdg.default_value) {
2361 return_value = str_replace ("~", home, xdg.default_value);
2363 return_value = NULL;
2366 return_value = str_replace("~", home, actual_value);
2369 return return_value;
2373 find_xdg_file (int xdg_type, char* filename) {
2374 /* xdg_type = 0 => config
2375 xdg_type = 1 => data
2376 xdg_type = 2 => cache*/
2378 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2379 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2382 gchar* temporary_string;
2386 if (! file_exists (temporary_file) && xdg_type != 2) {
2387 buf = get_xdg_var (XDG[3 + xdg_type]);
2388 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2391 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2392 g_free (temporary_file);
2393 temporary_file = g_strconcat (temporary_string, filename, NULL);
2397 //g_free (temporary_string); - segfaults.
2399 if (file_exists (temporary_file)) {
2400 return temporary_file;
2407 State *s = &uzbl.state;
2408 Network *n = &uzbl.net;
2410 for (i = 0; default_config[i].command != NULL; i++) {
2411 parse_cmd_line(default_config[i].command, NULL);
2414 if (g_strcmp0(s->config_file, "-") == 0) {
2415 s->config_file = NULL;
2419 else if (!s->config_file) {
2420 s->config_file = find_xdg_file (0, "/uzbl/config");
2423 if (s->config_file) {
2424 GArray* lines = read_file_by_line (s->config_file);
2428 while ((line = g_array_index(lines, gchar*, i))) {
2429 parse_cmd_line (line, NULL);
2433 g_array_free (lines, TRUE);
2435 if (uzbl.state.verbose)
2436 printf ("No configuration file loaded.\n");
2439 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2442 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2445 if (!uzbl.behave.cookie_handler)
2448 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2449 GString *s = g_string_new ("");
2450 SoupURI * soup_uri = soup_message_get_uri(msg);
2451 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2452 run_handler(uzbl.behave.cookie_handler, s->str);
2454 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2455 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2456 if ( p != NULL ) *p = '\0';
2457 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2459 if (uzbl.comm.sync_stdout)
2460 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2462 g_string_free(s, TRUE);
2466 save_cookies (SoupMessage *msg, gpointer user_data){
2470 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2471 cookie = soup_cookie_to_set_cookie_header(ck->data);
2472 SoupURI * soup_uri = soup_message_get_uri(msg);
2473 GString *s = g_string_new ("");
2474 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2475 run_handler(uzbl.behave.cookie_handler, s->str);
2477 g_string_free(s, TRUE);
2482 /* --- WEBINSPECTOR --- */
2484 hide_window_cb(GtkWidget *widget, gpointer data) {
2487 gtk_widget_hide(widget);
2491 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2494 (void) web_inspector;
2495 GtkWidget* scrolled_window;
2496 GtkWidget* new_web_view;
2499 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2500 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2501 G_CALLBACK(hide_window_cb), NULL);
2503 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2504 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2505 gtk_widget_show(g->inspector_window);
2507 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2508 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2509 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2510 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2511 gtk_widget_show(scrolled_window);
2513 new_web_view = webkit_web_view_new();
2514 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2516 return WEBKIT_WEB_VIEW(new_web_view);
2520 inspector_show_window_cb (WebKitWebInspector* inspector){
2522 gtk_widget_show(uzbl.gui.inspector_window);
2526 /* TODO: Add variables and code to make use of these functions */
2528 inspector_close_window_cb (WebKitWebInspector* inspector){
2534 inspector_attach_window_cb (WebKitWebInspector* inspector){
2540 inspector_detach_window_cb (WebKitWebInspector* inspector){
2546 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2552 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2558 set_up_inspector() {
2560 WebKitWebSettings *settings = view_settings();
2561 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2563 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2564 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2565 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2566 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2567 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2568 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2569 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2571 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2575 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2577 uzbl_cmdprop *c = v;
2582 if(c->type == TYPE_STR)
2583 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2584 else if(c->type == TYPE_INT)
2585 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2586 else if(c->type == TYPE_FLOAT)
2587 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2591 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2595 printf("bind %s = %s %s\n", (char *)k ,
2596 (char *)a->name, a->param?(char *)a->param:"");
2601 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2602 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2606 retreive_geometry() {
2608 GString *buf = g_string_new("");
2610 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2611 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2613 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2615 if(uzbl.gui.geometry)
2616 g_free(uzbl.gui.geometry);
2617 uzbl.gui.geometry = g_string_free(buf, FALSE);
2620 /* set up gtk, gobject, variable defaults and other things that tests and other
2621 * external applications need to do anyhow */
2623 initialize(int argc, char *argv[]) {
2624 gtk_init (&argc, &argv);
2625 if (!g_thread_supported ())
2626 g_thread_init (NULL);
2627 uzbl.state.executable_path = g_strdup(argv[0]);
2628 uzbl.state.selected_url = NULL;
2629 uzbl.state.searchtx = NULL;
2631 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2632 g_option_context_add_main_entries (context, entries, NULL);
2633 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2634 g_option_context_parse (context, &argc, &argv, NULL);
2635 g_option_context_free(context);
2637 if (uzbl.behave.print_version) {
2638 printf("Commit: %s\n", COMMIT);
2642 /* initialize hash table */
2643 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2645 uzbl.net.soup_session = webkit_get_default_session();
2646 uzbl.state.keycmd = g_strdup("");
2648 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2649 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2650 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2651 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2652 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2653 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2655 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2656 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2657 uzbl.gui.sbar.progress_w = 10;
2659 /* HTML mode defaults*/
2660 uzbl.behave.html_buffer = g_string_new("");
2661 uzbl.behave.html_endmarker = g_strdup(".");
2662 uzbl.behave.html_timeout = 60;
2663 uzbl.behave.base_url = g_strdup("http://invalid");
2665 /* default mode indicators */
2666 uzbl.behave.insert_indicator = g_strdup("I");
2667 uzbl.behave.cmd_indicator = g_strdup("C");
2669 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2670 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2671 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2672 uzbl.info.arch = ARCH;
2673 uzbl.info.commit = COMMIT;
2676 make_var_to_name_hash();
2678 uzbl.gui.scrolled_win = create_browser();
2681 #ifndef UZBL_LIBRARY
2684 main (int argc, char* argv[]) {
2685 initialize(argc, argv);
2687 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2691 /* initial packing */
2692 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2693 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2695 if (uzbl.state.socket_id) {
2696 uzbl.gui.plug = create_plug ();
2697 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2698 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2700 uzbl.gui.main_window = create_window ();
2701 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2702 gtk_widget_show_all (uzbl.gui.main_window);
2703 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2706 if(!uzbl.state.instance_name)
2707 uzbl.state.instance_name = itos((int)uzbl.xwin);
2709 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2711 if (uzbl.state.verbose) {
2712 printf("Uzbl start location: %s\n", argv[0]);
2713 if (uzbl.state.socket_id)
2714 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2716 printf("window_id %i\n",(int) uzbl.xwin);
2717 printf("pid %i\n", getpid ());
2718 printf("name: %s\n", uzbl.state.instance_name);
2721 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2722 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2723 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2724 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2725 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2727 if(uzbl.gui.geometry)
2730 retreive_geometry();
2732 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2733 if (argc > 1 && !uzbl.state.uri)
2734 uri_override = g_strdup(argv[1]);
2735 gboolean verbose_override = uzbl.state.verbose;
2738 set_insert_mode(FALSE);
2740 if (!uzbl.behave.show_status)
2741 gtk_widget_hide(uzbl.gui.mainbar);
2748 if (verbose_override > uzbl.state.verbose)
2749 uzbl.state.verbose = verbose_override;
2752 set_var_value("uri", uri_override);
2753 g_free(uri_override);
2754 } else if (uzbl.state.uri)
2760 return EXIT_SUCCESS;
2764 /* vi: set et ts=4: */