1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
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 /* associate command names to their properties */
84 typedef const struct {
85 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
86 the PTR() macro is kind of preventing this change at the moment. */
94 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
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_title)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
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;
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(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;
236 * recurse == 1: don't expand '@(command)@'
237 * recurse == 2: don't expand '@<java script>@'
240 expand(char *s, guint recurse) {
244 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
249 gchar *cmd_stdout = NULL;
251 GString *buf = g_string_new("");
252 GString *js_ret = g_string_new("");
257 g_string_append_c(buf, *++s);
262 etype = get_exp_type(s);
267 vend = strpbrk(s, end_simple_var);
268 if(!vend) vend = strchr(s, '\0');
272 vend = strchr(s, upto);
273 if(!vend) vend = strchr(s, '\0');
277 strcpy(str_end, ")@");
279 vend = strstr(s, str_end);
280 if(!vend) vend = strchr(s, '\0');
284 strcpy(str_end, ">@");
286 vend = strstr(s, str_end);
287 if(!vend) vend = strchr(s, '\0');
291 strcpy(str_end, "]@");
293 vend = strstr(s, str_end);
294 if(!vend) vend = strchr(s, '\0');
299 strncpy(ret, s, vend-s);
303 if(etype == EXP_SIMPLE_VAR ||
304 etype == EXP_BRACED_VAR) {
305 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
306 if(c->type == TYPE_STR && *c->ptr != NULL) {
307 g_string_append(buf, (gchar *)*c->ptr);
308 } else if(c->type == TYPE_INT) {
309 g_string_append_printf(buf, "%d", (int)*c->ptr);
311 else if(c->type == TYPE_FLOAT) {
312 g_string_append_printf(buf, "%f", *(float *)c->ptr);
316 if(etype == EXP_SIMPLE_VAR)
321 else if(recurse != 1 &&
323 mycmd = expand(ret, 1);
324 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
328 g_printerr("error on running command: %s\n", err->message);
331 else if (*cmd_stdout) {
332 int len = strlen(cmd_stdout);
334 if(cmd_stdout[len-1] == '\n')
335 cmd_stdout[--len] = 0; /* strip trailing newline */
337 g_string_append(buf, cmd_stdout);
342 else if(recurse != 2 &&
344 mycmd = expand(ret, 2);
345 eval_js(uzbl.gui.web_view, mycmd, js_ret);
349 g_string_append(buf, js_ret->str);
350 g_string_free(js_ret, TRUE);
351 js_ret = g_string_new("");
355 else if(etype == EXP_ESCAPE) {
356 mycmd = expand(ret, 0);
357 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
359 g_string_append(buf, escaped);
368 g_string_append_c(buf, *s);
373 g_string_free(js_ret, TRUE);
374 return g_string_free(buf, FALSE);
381 snprintf(tmp, sizeof(tmp), "%i", val);
382 return g_strdup(tmp);
386 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
389 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
392 str_replace (const char* search, const char* replace, const char* string) {
396 buf = g_strsplit (string, search, -1);
397 ret = g_strjoinv (replace, buf);
398 g_strfreev(buf); // somebody said this segfaults
404 read_file_by_line (gchar *path) {
405 GIOChannel *chan = NULL;
406 gchar *readbuf = NULL;
408 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
411 chan = g_io_channel_new_file(path, "r", NULL);
414 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
415 const gchar* val = g_strdup (readbuf);
416 g_array_append_val (lines, val);
421 g_io_channel_unref (chan);
423 fprintf(stderr, "File '%s' not be read.\n", path);
430 parseenv (char* string) {
431 extern char** environ;
432 gchar* tmpstr = NULL;
436 while (environ[i] != NULL) {
437 gchar** env = g_strsplit (environ[i], "=", 2);
438 gchar* envname = g_strconcat ("$", env[0], NULL);
440 if (g_strrstr (string, envname) != NULL) {
441 tmpstr = g_strdup(string);
443 string = str_replace(envname, env[1], tmpstr);
448 g_strfreev (env); // somebody said this breaks uzbl
456 setup_signal(int signr, sigfunc *shandler) {
457 struct sigaction nh, oh;
459 nh.sa_handler = shandler;
460 sigemptyset(&nh.sa_mask);
463 if(sigaction(signr, &nh, &oh) < 0)
471 if (uzbl.behave.fifo_dir)
472 unlink (uzbl.comm.fifo_path);
473 if (uzbl.behave.socket_dir)
474 unlink (uzbl.comm.socket_path);
476 g_free(uzbl.state.executable_path);
477 g_free(uzbl.state.keycmd);
478 g_hash_table_destroy(uzbl.bindings);
479 g_hash_table_destroy(uzbl.behave.commands);
480 g_scanner_destroy(uzbl.scan);
483 /* used for html_mode_timeout
484 * be sure to extend this function to use
485 * more timers if needed in other places
488 set_timeout(int seconds) {
490 memset(&t, 0, sizeof t);
492 t.it_value.tv_sec = seconds;
493 t.it_value.tv_usec = 0;
494 setitimer(ITIMER_REAL, &t, NULL);
497 /* --- SIGNAL HANDLER --- */
500 catch_sigterm(int s) {
506 catch_sigint(int s) {
516 set_var_value("mode", "0");
521 /* --- CALLBACKS --- */
524 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527 (void) navigation_action;
528 (void) policy_decision;
530 const gchar* uri = webkit_network_request_get_uri (request);
531 if (uzbl.state.verbose)
532 printf("New window requested -> %s \n", uri);
533 webkit_web_policy_decision_use(policy_decision);
538 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
543 /* If we can display it, let's display it... */
544 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
545 webkit_web_policy_decision_use (policy_decision);
549 /* ...everything we can't displayed is downloaded */
550 webkit_web_policy_decision_download (policy_decision);
555 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
559 if (uzbl.state.selected_url != NULL) {
560 if (uzbl.state.verbose)
561 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
562 new_window_load_uri(uzbl.state.selected_url);
564 if (uzbl.state.verbose)
565 printf("New web view -> %s\n","Nothing to open, exiting");
571 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
574 if (uzbl.behave.download_handler) {
575 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
576 if (uzbl.state.verbose)
577 printf("Download -> %s\n",uri);
578 /* if urls not escaped, we may have to escape and quote uri before this call */
579 run_handler(uzbl.behave.download_handler, uri);
584 /* scroll a bar in a given direction */
586 scroll (GtkAdjustment* bar, GArray *argv) {
590 gdouble page_size = gtk_adjustment_get_page_size(bar);
591 gdouble value = gtk_adjustment_get_value(bar);
592 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
595 value += page_size * amount * 0.01;
599 max_value = gtk_adjustment_get_upper(bar) - page_size;
601 if (value > max_value)
602 value = max_value; /* don't scroll past the end of the page */
604 gtk_adjustment_set_value (bar, value);
608 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
609 (void) page; (void) argv; (void) result;
610 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
614 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
615 (void) page; (void) argv; (void) result;
616 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
617 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
621 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
622 (void) page; (void) result;
623 scroll(uzbl.gui.bar_v, argv);
627 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
628 (void) page; (void) result;
629 scroll(uzbl.gui.bar_h, argv);
634 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
635 if(uzbl.state.verbose)
636 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
638 /* update geometry var with the actual geometry
639 this is necessary as some WMs don't seem to honour
640 the above setting and we don't want to end up with
641 wrong geometry information
648 if (!uzbl.behave.show_status) {
649 gtk_widget_hide(uzbl.gui.mainbar);
651 gtk_widget_show(uzbl.gui.mainbar);
657 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
662 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
666 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
671 if (uzbl.behave.show_status) {
672 gtk_widget_hide(uzbl.gui.mainbar);
674 gtk_widget_show(uzbl.gui.mainbar);
676 uzbl.behave.show_status = !uzbl.behave.show_status;
681 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
685 //Set selected_url state variable
686 g_free(uzbl.state.selected_url);
687 uzbl.state.selected_url = NULL;
689 uzbl.state.selected_url = g_strdup(link);
695 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
698 const gchar *title = webkit_web_view_get_title(web_view);
699 if (uzbl.gui.main_title)
700 g_free (uzbl.gui.main_title);
701 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
706 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
709 uzbl.gui.sbar.load_progress = progress;
711 g_free(uzbl.gui.sbar.progress_bar);
712 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
718 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
722 if (uzbl.behave.load_finish_handler)
723 run_handler(uzbl.behave.load_finish_handler, "");
726 void clear_keycmd() {
727 g_free(uzbl.state.keycmd);
728 uzbl.state.keycmd = g_strdup("");
732 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
736 uzbl.gui.sbar.load_progress = 0;
737 clear_keycmd(); // don't need old commands to remain on new page?
738 if (uzbl.behave.load_start_handler)
739 run_handler(uzbl.behave.load_start_handler, "");
743 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
746 g_free (uzbl.state.uri);
747 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
748 uzbl.state.uri = g_string_free (newuri, FALSE);
749 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
750 set_insert_mode(uzbl.behave.always_insert_mode);
753 if (uzbl.behave.load_commit_handler)
754 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
758 destroy_cb (GtkWidget* widget, gpointer data) {
766 if (uzbl.behave.history_handler) {
768 struct tm * timeinfo;
771 timeinfo = localtime ( &rawtime );
772 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
773 run_handler(uzbl.behave.history_handler, date);
778 /* VIEW funcs (little webkit wrappers) */
779 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
781 VIEWFUNC(reload_bypass_cache)
782 VIEWFUNC(stop_loading)
789 /* -- command to callback/function map for things we cannot attach to any signals */
790 struct {char *key; CommandInfo value;} cmdlist[] =
791 { /* key function no_split */
792 { "back", {view_go_back, 0} },
793 { "forward", {view_go_forward, 0} },
794 { "scroll_vert", {scroll_vert, 0} },
795 { "scroll_horz", {scroll_horz, 0} },
796 { "scroll_begin", {scroll_begin, 0} },
797 { "scroll_end", {scroll_end, 0} },
798 { "reload", {view_reload, 0}, },
799 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
800 { "stop", {view_stop_loading, 0}, },
801 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
802 { "zoom_out", {view_zoom_out, 0}, },
803 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
804 { "uri", {load_uri, TRUE} },
805 { "js", {run_js, TRUE} },
806 { "script", {run_external_js, 0} },
807 { "toggle_status", {toggle_status_cb, 0} },
808 { "spawn", {spawn, 0} },
809 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
810 { "sh", {spawn_sh, 0} },
811 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
812 { "exit", {close_uzbl, 0} },
813 { "search", {search_forward_text, TRUE} },
814 { "search_reverse", {search_reverse_text, TRUE} },
815 { "dehilight", {dehilight, 0} },
816 { "toggle_insert_mode", {toggle_insert_mode, 0} },
817 { "set", {set_var, TRUE} },
818 //{ "get", {get_var, TRUE} },
819 { "bind", {act_bind, TRUE} },
820 { "dump_config", {act_dump_config, 0} },
821 { "keycmd", {keycmd, TRUE} },
822 { "keycmd_nl", {keycmd_nl, TRUE} },
823 { "keycmd_bs", {keycmd_bs, 0} },
824 { "chain", {chain, 0} },
825 { "print", {print, TRUE} }
832 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
834 for (i = 0; i < LENGTH(cmdlist); i++)
835 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
838 /* -- CORE FUNCTIONS -- */
841 free_action(gpointer act) {
842 Action *action = (Action*)act;
843 g_free(action->name);
845 g_free(action->param);
850 new_action(const gchar *name, const gchar *param) {
851 Action *action = g_new(Action, 1);
853 action->name = g_strdup(name);
855 action->param = g_strdup(param);
857 action->param = NULL;
863 file_exists (const char * filename) {
864 return (access(filename, F_OK) == 0);
868 set_var(WebKitWebView *page, GArray *argv, GString *result) {
869 (void) page; (void) result;
870 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
871 if (split[0] != NULL) {
872 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
873 set_var_value(g_strstrip(split[0]), value);
880 print(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) result;
884 buf = expand(argv_idx(argv, 0), 0);
885 g_string_assign(result, buf);
890 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
891 (void) page; (void) result;
892 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
893 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
894 add_binding(g_strstrip(split[0]), value);
912 set_mode_indicator() {
913 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
914 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
918 set_insert_mode(gboolean mode) {
919 uzbl.behave.insert_mode = mode;
920 set_mode_indicator();
924 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
925 (void) page; (void) result;
927 if (argv_idx(argv, 0)) {
928 if (strcmp (argv_idx(argv, 0), "0") == 0) {
929 set_insert_mode(FALSE);
931 set_insert_mode(TRUE);
934 set_insert_mode( !uzbl.behave.insert_mode );
941 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
944 if (argv_idx(argv, 0)) {
945 GString* newuri = g_string_new (argv_idx(argv, 0));
946 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
947 run_js(web_view, argv, NULL);
950 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
951 g_string_prepend (newuri, "http://");
952 /* if we do handle cookies, ask our handler for them */
953 webkit_web_view_load_uri (web_view, newuri->str);
954 g_string_free (newuri, TRUE);
961 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
962 size_t argumentCount, const JSValueRef arguments[],
963 JSValueRef* exception) {
968 JSStringRef js_result_string;
969 GString *result = g_string_new("");
971 if (argumentCount >= 1) {
972 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
973 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
974 char ctl_line[arg_size];
975 JSStringGetUTF8CString(arg, ctl_line, arg_size);
977 parse_cmd_line(ctl_line, result);
979 JSStringRelease(arg);
981 js_result_string = JSStringCreateWithUTF8CString(result->str);
983 g_string_free(result, TRUE);
985 return JSValueMakeString(ctx, js_result_string);
988 JSStaticFunction js_static_functions[] = {
989 {"run", js_run_command, kJSPropertyAttributeNone},
994 /* This function creates the class and its definition, only once */
995 if (!uzbl.js.initialized) {
996 /* it would be pretty cool to make this dynamic */
997 uzbl.js.classdef = kJSClassDefinitionEmpty;
998 uzbl.js.classdef.staticFunctions = js_static_functions;
1000 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1006 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1007 WebKitWebFrame *frame;
1008 JSGlobalContextRef context;
1009 JSObjectRef globalobject;
1010 JSStringRef var_name;
1012 JSStringRef js_script;
1013 JSValueRef js_result;
1014 JSStringRef js_result_string;
1015 size_t js_result_size;
1019 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1020 context = webkit_web_frame_get_global_context(frame);
1021 globalobject = JSContextGetGlobalObject(context);
1023 /* uzbl javascript namespace */
1024 var_name = JSStringCreateWithUTF8CString("Uzbl");
1025 JSObjectSetProperty(context, globalobject, var_name,
1026 JSObjectMake(context, uzbl.js.classref, NULL),
1027 kJSClassAttributeNone, NULL);
1029 /* evaluate the script and get return value*/
1030 js_script = JSStringCreateWithUTF8CString(script);
1031 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1032 if (js_result && !JSValueIsUndefined(context, js_result)) {
1033 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1034 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1036 if (js_result_size) {
1037 char js_result_utf8[js_result_size];
1038 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1039 g_string_assign(result, js_result_utf8);
1042 JSStringRelease(js_result_string);
1046 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1048 JSStringRelease(var_name);
1049 JSStringRelease(js_script);
1053 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1054 if (argv_idx(argv, 0))
1055 eval_js(web_view, argv_idx(argv, 0), result);
1059 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1061 if (argv_idx(argv, 0)) {
1062 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1067 while ((line = g_array_index(lines, gchar*, i))) {
1069 js = g_strdup (line);
1071 gchar* newjs = g_strconcat (js, line, NULL);
1078 if (uzbl.state.verbose)
1079 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1081 if (argv_idx (argv, 1)) {
1082 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1086 eval_js (web_view, js, result);
1088 g_array_free (lines, TRUE);
1093 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1094 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1095 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1096 webkit_web_view_unmark_text_matches (page);
1097 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1098 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1102 if (uzbl.state.searchtx) {
1103 if (uzbl.state.verbose)
1104 printf ("Searching: %s\n", uzbl.state.searchtx);
1105 webkit_web_view_set_highlight_text_matches (page, TRUE);
1106 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1111 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1113 search_text(page, argv, TRUE);
1117 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1119 search_text(page, argv, FALSE);
1123 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1124 (void) argv; (void) result;
1125 webkit_web_view_set_highlight_text_matches (page, FALSE);
1130 new_window_load_uri (const gchar * uri) {
1131 if (uzbl.behave.new_window) {
1132 GString *s = g_string_new ("");
1133 g_string_printf(s, "'%s'", uri);
1134 run_handler(uzbl.behave.new_window, s->str);
1137 GString* to_execute = g_string_new ("");
1138 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1140 for (i = 0; entries[i].long_name != NULL; i++) {
1141 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1142 gchar** str = (gchar**)entries[i].arg_data;
1144 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1148 if (uzbl.state.verbose)
1149 printf("\n%s\n", to_execute->str);
1150 g_spawn_command_line_async (to_execute->str, NULL);
1151 g_string_free (to_execute, TRUE);
1155 chain (WebKitWebView *page, GArray *argv, GString *result) {
1156 (void) page; (void) result;
1158 gchar **parts = NULL;
1160 while ((a = argv_idx(argv, i++))) {
1161 parts = g_strsplit (a, " ", 2);
1163 parse_command(parts[0], parts[1], result);
1169 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1173 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1179 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1183 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1189 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1194 int len = strlen(uzbl.state.keycmd);
1195 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1197 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1202 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1209 /* --Statusbar functions-- */
1211 build_progressbar_ascii(int percent) {
1212 int width=uzbl.gui.sbar.progress_w;
1215 GString *bar = g_string_new("");
1217 l = (double)percent*((double)width/100.);
1218 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1220 for(i=0; i<(int)l; i++)
1221 g_string_append(bar, uzbl.gui.sbar.progress_s);
1224 g_string_append(bar, uzbl.gui.sbar.progress_u);
1226 return g_string_free(bar, FALSE);
1228 /* --End Statusbar functions-- */
1231 sharg_append(GArray *a, const gchar *str) {
1232 const gchar *s = (str ? str : "");
1233 g_array_append_val(a, s);
1236 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1238 run_command (const gchar *command, const guint npre, const gchar **args,
1239 const gboolean sync, char **output_stdout) {
1240 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1243 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1244 gchar *pid = itos(getpid());
1245 gchar *xwin = itos(uzbl.xwin);
1247 sharg_append(a, command);
1248 for (i = 0; i < npre; i++) /* add n args before the default vars */
1249 sharg_append(a, args[i]);
1250 sharg_append(a, uzbl.state.config_file);
1251 sharg_append(a, pid);
1252 sharg_append(a, xwin);
1253 sharg_append(a, uzbl.comm.fifo_path);
1254 sharg_append(a, uzbl.comm.socket_path);
1255 sharg_append(a, uzbl.state.uri);
1256 sharg_append(a, uzbl.gui.main_title);
1258 for (i = npre; i < g_strv_length((gchar**)args); i++)
1259 sharg_append(a, args[i]);
1263 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1265 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1266 NULL, NULL, output_stdout, NULL, NULL, &err);
1267 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1268 NULL, NULL, NULL, &err);
1270 if (uzbl.state.verbose) {
1271 GString *s = g_string_new("spawned:");
1272 for (i = 0; i < (a->len); i++) {
1273 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1274 g_string_append_printf(s, " %s", qarg);
1277 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1278 printf("%s\n", s->str);
1279 g_string_free(s, TRUE);
1281 printf("Stdout: %s\n", *output_stdout);
1285 g_printerr("error on run_command: %s\n", err->message);
1290 g_array_free (a, TRUE);
1295 split_quoted(const gchar* src, const gboolean unquote) {
1296 /* split on unquoted space, return array of strings;
1297 remove a layer of quotes and backslashes if unquote */
1298 if (!src) return NULL;
1300 gboolean dq = FALSE;
1301 gboolean sq = FALSE;
1302 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1303 GString *s = g_string_new ("");
1307 for (p = src; *p != '\0'; p++) {
1308 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1309 else if (*p == '\\') { g_string_append_c(s, *p++);
1310 g_string_append_c(s, *p); }
1311 else if ((*p == '"') && unquote && !sq) dq = !dq;
1312 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1314 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1315 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1317 else if ((*p == ' ') && !dq && !sq) {
1318 dup = g_strdup(s->str);
1319 g_array_append_val(a, dup);
1320 g_string_truncate(s, 0);
1321 } else g_string_append_c(s, *p);
1323 dup = g_strdup(s->str);
1324 g_array_append_val(a, dup);
1325 ret = (gchar**)a->data;
1326 g_array_free (a, FALSE);
1327 g_string_free (s, TRUE);
1332 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1333 (void)web_view; (void)result;
1334 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1335 if (argv_idx(argv, 0))
1336 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1340 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1341 (void)web_view; (void)result;
1343 if (argv_idx(argv, 0))
1344 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1345 TRUE, &uzbl.comm.sync_stdout);
1349 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1350 (void)web_view; (void)result;
1351 if (!uzbl.behave.shell_cmd) {
1352 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1357 gchar *spacer = g_strdup("");
1358 g_array_insert_val(argv, 1, spacer);
1359 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1361 for (i = 1; i < g_strv_length(cmd); i++)
1362 g_array_prepend_val(argv, cmd[i]);
1364 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1370 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1371 (void)web_view; (void)result;
1372 if (!uzbl.behave.shell_cmd) {
1373 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1378 gchar *spacer = g_strdup("");
1379 g_array_insert_val(argv, 1, spacer);
1380 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1382 for (i = 1; i < g_strv_length(cmd); i++)
1383 g_array_prepend_val(argv, cmd[i]);
1385 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1386 TRUE, &uzbl.comm.sync_stdout);
1392 parse_command(const char *cmd, const char *param, GString *result) {
1395 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1397 gchar **par = split_quoted(param, TRUE);
1398 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1400 if (c->no_split) { /* don't split */
1401 sharg_append(a, param);
1403 for (i = 0; i < g_strv_length(par); i++)
1404 sharg_append(a, par[i]);
1407 if (result == NULL) {
1408 GString *result_print = g_string_new("");
1410 c->function(uzbl.gui.web_view, a, result_print);
1411 if (result_print->len)
1412 printf("%*s\n", result_print->len, result_print->str);
1414 g_string_free(result_print, TRUE);
1416 c->function(uzbl.gui.web_view, a, result);
1419 g_array_free (a, TRUE);
1422 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1429 if(*uzbl.net.proxy_url == ' '
1430 || uzbl.net.proxy_url == NULL) {
1431 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1432 (GType) SOUP_SESSION_PROXY_URI);
1435 suri = soup_uri_new(uzbl.net.proxy_url);
1436 g_object_set(G_OBJECT(uzbl.net.soup_session),
1437 SOUP_SESSION_PROXY_URI,
1439 soup_uri_free(suri);
1446 if(file_exists(uzbl.gui.icon)) {
1447 if (uzbl.gui.main_window)
1448 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1450 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1456 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1457 g_array_append_val (a, uzbl.state.uri);
1458 load_uri(uzbl.gui.web_view, a, NULL);
1459 g_array_free (a, TRUE);
1463 cmd_always_insert_mode() {
1464 set_insert_mode(uzbl.behave.always_insert_mode);
1470 g_object_set(G_OBJECT(uzbl.net.soup_session),
1471 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1475 cmd_max_conns_host() {
1476 g_object_set(G_OBJECT(uzbl.net.soup_session),
1477 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1482 soup_session_remove_feature
1483 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1484 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1485 /*g_free(uzbl.net.soup_logger);*/
1487 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1488 soup_session_add_feature(uzbl.net.soup_session,
1489 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1494 return webkit_web_view_get_settings(uzbl.gui.web_view);
1499 WebKitWebSettings *ws = view_settings();
1500 if (uzbl.behave.font_size > 0) {
1501 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1504 if (uzbl.behave.monospace_size > 0) {
1505 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1506 uzbl.behave.monospace_size, NULL);
1508 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1509 uzbl.behave.font_size, NULL);
1515 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1519 cmd_disable_plugins() {
1520 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1521 !uzbl.behave.disable_plugins, NULL);
1525 cmd_disable_scripts() {
1526 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1527 !uzbl.behave.disable_scripts, NULL);
1531 cmd_minimum_font_size() {
1532 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1533 uzbl.behave.minimum_font_size, NULL);
1536 cmd_autoload_img() {
1537 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1538 uzbl.behave.autoload_img, NULL);
1543 cmd_autoshrink_img() {
1544 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1545 uzbl.behave.autoshrink_img, NULL);
1550 cmd_enable_spellcheck() {
1551 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1552 uzbl.behave.enable_spellcheck, NULL);
1556 cmd_enable_private() {
1557 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1558 uzbl.behave.enable_private, NULL);
1563 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1564 uzbl.behave.print_bg, NULL);
1569 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1570 uzbl.behave.style_uri, NULL);
1574 cmd_resizable_txt() {
1575 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1576 uzbl.behave.resizable_txt, NULL);
1580 cmd_default_encoding() {
1581 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1582 uzbl.behave.default_encoding, NULL);
1586 cmd_enforce_96dpi() {
1587 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1588 uzbl.behave.enforce_96dpi, NULL);
1592 cmd_caret_browsing() {
1593 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1594 uzbl.behave.caret_browsing, NULL);
1598 cmd_cookie_handler() {
1599 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1600 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1601 if ((g_strcmp0(split[0], "sh") == 0) ||
1602 (g_strcmp0(split[0], "spawn") == 0)) {
1603 g_free (uzbl.behave.cookie_handler);
1604 uzbl.behave.cookie_handler =
1605 g_strdup_printf("sync_%s %s", split[0], split[1]);
1612 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1613 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1614 if ((g_strcmp0(split[0], "sh") == 0) ||
1615 (g_strcmp0(split[0], "spawn") == 0)) {
1616 g_free (uzbl.behave.new_window);
1617 uzbl.behave.new_window =
1618 g_strdup_printf("%s %s", split[0], split[1]);
1625 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1630 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1635 if(uzbl.behave.inject_html) {
1636 webkit_web_view_load_html_string (uzbl.gui.web_view,
1637 uzbl.behave.inject_html, NULL);
1646 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1647 uzbl.behave.modmask = 0;
1649 if(uzbl.behave.modkey)
1650 g_free(uzbl.behave.modkey);
1651 uzbl.behave.modkey = buf;
1653 for (i = 0; modkeys[i].key != NULL; i++) {
1654 if (g_strrstr(buf, modkeys[i].key))
1655 uzbl.behave.modmask |= modkeys[i].mask;
1661 if (*uzbl.net.useragent == ' ') {
1662 g_free (uzbl.net.useragent);
1663 uzbl.net.useragent = NULL;
1665 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1666 uzbl.net.useragent, NULL);
1672 gtk_widget_ref(uzbl.gui.scrolled_win);
1673 gtk_widget_ref(uzbl.gui.mainbar);
1674 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1675 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1677 if(uzbl.behave.status_top) {
1678 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1679 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.scrolled_win, TRUE, TRUE, 0);
1683 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1685 gtk_widget_unref(uzbl.gui.scrolled_win);
1686 gtk_widget_unref(uzbl.gui.mainbar);
1687 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1692 set_var_value(gchar *name, gchar *val) {
1693 uzbl_cmdprop *c = NULL;
1697 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1698 if(!c->writeable) return FALSE;
1700 /* check for the variable type */
1701 if (c->type == TYPE_STR) {
1702 buf = expand(val, 0);
1705 } else if(c->type == TYPE_INT) {
1706 int *ip = (int *)c->ptr;
1707 buf = expand(val, 0);
1708 *ip = (int)strtoul(buf, &endp, 10);
1710 } else if (c->type == TYPE_FLOAT) {
1711 float *fp = (float *)c->ptr;
1712 buf = expand(val, 0);
1713 *fp = strtod(buf, &endp);
1717 /* invoke a command specific function */
1718 if(c->func) c->func();
1725 Behaviour *b = &uzbl.behave;
1727 if(b->html_buffer->str) {
1728 webkit_web_view_load_html_string (uzbl.gui.web_view,
1729 b->html_buffer->str, b->base_url);
1730 g_string_free(b->html_buffer, TRUE);
1731 b->html_buffer = g_string_new("");
1735 enum {M_CMD, M_HTML};
1737 parse_cmd_line(const char *ctl_line, GString *result) {
1738 Behaviour *b = &uzbl.behave;
1741 if(b->mode == M_HTML) {
1742 len = strlen(b->html_endmarker);
1743 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1744 if(len == strlen(ctl_line)-1 &&
1745 !strncmp(b->html_endmarker, ctl_line, len)) {
1747 set_var_value("mode", "0");
1752 set_timeout(b->html_timeout);
1753 g_string_append(b->html_buffer, ctl_line);
1756 else if((ctl_line[0] == '#') /* Comments */
1757 || (ctl_line[0] == ' ')
1758 || (ctl_line[0] == '\n'))
1759 ; /* ignore these lines */
1760 else { /* parse a command */
1762 gchar **tokens = NULL;
1763 len = strlen(ctl_line);
1765 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1766 ctlstrip = g_strndup(ctl_line, len - 1);
1767 else ctlstrip = g_strdup(ctl_line);
1769 tokens = g_strsplit(ctlstrip, " ", 2);
1770 parse_command(tokens[0], tokens[1], result);
1777 build_stream_name(int type, const gchar* dir) {
1778 State *s = &uzbl.state;
1782 str = g_strdup_printf
1783 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1784 } else if (type == SOCKET) {
1785 str = g_strdup_printf
1786 ("%s/uzbl_socket_%s", dir, s->instance_name);
1792 control_fifo(GIOChannel *gio, GIOCondition condition) {
1793 if (uzbl.state.verbose)
1794 printf("triggered\n");
1799 if (condition & G_IO_HUP)
1800 g_error ("Fifo: Read end of pipe died!\n");
1803 g_error ("Fifo: GIOChannel broke\n");
1805 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1806 if (ret == G_IO_STATUS_ERROR) {
1807 g_error ("Fifo: Error reading: %s\n", err->message);
1811 parse_cmd_line(ctl_line, NULL);
1818 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1819 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1820 if (unlink(uzbl.comm.fifo_path) == -1)
1821 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1822 g_free(uzbl.comm.fifo_path);
1823 uzbl.comm.fifo_path = NULL;
1826 GIOChannel *chan = NULL;
1827 GError *error = NULL;
1828 gchar *path = build_stream_name(FIFO, dir);
1830 if (!file_exists(path)) {
1831 if (mkfifo (path, 0666) == 0) {
1832 // 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.
1833 chan = g_io_channel_new_file(path, "r+", &error);
1835 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1836 if (uzbl.state.verbose)
1837 printf ("init_fifo: created successfully as %s\n", path);
1838 uzbl.comm.fifo_path = path;
1840 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1841 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1842 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1843 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1845 /* if we got this far, there was an error; cleanup */
1846 if (error) g_error_free (error);
1853 control_stdin(GIOChannel *gio, GIOCondition condition) {
1855 gchar *ctl_line = NULL;
1858 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1859 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1862 parse_cmd_line(ctl_line, NULL);
1870 GIOChannel *chan = NULL;
1871 GError *error = NULL;
1873 chan = g_io_channel_unix_new(fileno(stdin));
1875 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1876 g_error ("Stdin: could not add watch\n");
1878 if (uzbl.state.verbose)
1879 printf ("Stdin: watch added successfully\n");
1882 g_error ("Stdin: Error while opening: %s\n", error->message);
1884 if (error) g_error_free (error);
1888 control_socket(GIOChannel *chan) {
1889 struct sockaddr_un remote;
1890 unsigned int t = sizeof(remote);
1892 GIOChannel *clientchan;
1894 clientsock = accept (g_io_channel_unix_get_fd(chan),
1895 (struct sockaddr *) &remote, &t);
1897 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1898 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1899 (GIOFunc) control_client_socket, clientchan);
1906 control_client_socket(GIOChannel *clientchan) {
1908 GString *result = g_string_new("");
1909 GError *error = NULL;
1913 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1914 if (ret == G_IO_STATUS_ERROR) {
1915 g_warning ("Error reading: %s\n", error->message);
1916 g_io_channel_shutdown(clientchan, TRUE, &error);
1918 } else if (ret == G_IO_STATUS_EOF) {
1919 /* shutdown and remove channel watch from main loop */
1920 g_io_channel_shutdown(clientchan, TRUE, &error);
1925 parse_cmd_line (ctl_line, result);
1926 g_string_append_c(result, '\n');
1927 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1929 if (ret == G_IO_STATUS_ERROR) {
1930 g_warning ("Error writing: %s", error->message);
1932 g_io_channel_flush(clientchan, &error);
1935 if (error) g_error_free (error);
1936 g_string_free(result, TRUE);
1942 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1943 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1944 if (unlink(uzbl.comm.socket_path) == -1)
1945 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1946 g_free(uzbl.comm.socket_path);
1947 uzbl.comm.socket_path = NULL;
1955 GIOChannel *chan = NULL;
1957 struct sockaddr_un local;
1958 gchar *path = build_stream_name(SOCKET, dir);
1960 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1962 local.sun_family = AF_UNIX;
1963 strcpy (local.sun_path, path);
1964 unlink (local.sun_path);
1966 len = strlen (local.sun_path) + sizeof (local.sun_family);
1967 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1968 if (uzbl.state.verbose)
1969 printf ("init_socket: opened in %s\n", path);
1972 if( (chan = g_io_channel_unix_new(sock)) ) {
1973 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1974 uzbl.comm.socket_path = path;
1977 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1979 /* if we got this far, there was an error; cleanup */
1986 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1987 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1989 // this function may be called very early when the templates are not set (yet), hence the checks
1991 update_title (void) {
1992 Behaviour *b = &uzbl.behave;
1995 if (b->show_status) {
1996 if (b->title_format_short) {
1997 parsed = expand(b->title_format_short, 0);
1998 if (uzbl.gui.main_window)
1999 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2002 if (b->status_format) {
2003 parsed = expand(b->status_format, 0);
2004 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2007 if (b->status_background) {
2009 gdk_color_parse (b->status_background, &color);
2010 //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)
2011 if (uzbl.gui.main_window)
2012 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2013 else if (uzbl.gui.plug)
2014 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2017 if (b->title_format_long) {
2018 parsed = expand(b->title_format_long, 0);
2019 if (uzbl.gui.main_window)
2020 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2027 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2031 retreive_geometry();
2036 key_press_cb (GtkWidget* window, GdkEventKey* event)
2038 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2042 if (event->type != GDK_KEY_PRESS ||
2043 event->keyval == GDK_Page_Up ||
2044 event->keyval == GDK_Page_Down ||
2045 event->keyval == GDK_Up ||
2046 event->keyval == GDK_Down ||
2047 event->keyval == GDK_Left ||
2048 event->keyval == GDK_Right ||
2049 event->keyval == GDK_Shift_L ||
2050 event->keyval == GDK_Shift_R)
2053 /* turn off insert mode (if always_insert_mode is not used) */
2054 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2055 set_insert_mode(uzbl.behave.always_insert_mode);
2060 if (uzbl.behave.insert_mode &&
2061 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2062 (!uzbl.behave.modmask)
2067 if (event->keyval == GDK_Escape) {
2070 dehilight(uzbl.gui.web_view, NULL, NULL);
2074 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2075 if (event->keyval == GDK_Insert) {
2077 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2078 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2080 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2083 GString* keycmd = g_string_new(uzbl.state.keycmd);
2084 g_string_append (keycmd, str);
2085 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2092 if (event->keyval == GDK_BackSpace)
2093 keycmd_bs(NULL, NULL, NULL);
2095 gboolean key_ret = FALSE;
2096 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2099 GString* keycmd = g_string_new(uzbl.state.keycmd);
2100 g_string_append(keycmd, event->string);
2101 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2104 run_keycmd(key_ret);
2106 if (key_ret) return (!uzbl.behave.insert_mode);
2111 run_keycmd(const gboolean key_ret) {
2112 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2114 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2116 parse_command(act->name, act->param, NULL);
2120 /* try if it's an incremental keycmd or one that takes args, and run it */
2121 GString* short_keys = g_string_new ("");
2122 GString* short_keys_inc = g_string_new ("");
2124 guint len = strlen(uzbl.state.keycmd);
2125 for (i=0; i<len; i++) {
2126 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2127 g_string_assign(short_keys_inc, short_keys->str);
2128 g_string_append_c(short_keys, '_');
2129 g_string_append_c(short_keys_inc, '*');
2131 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2132 /* run normal cmds only if return was pressed */
2133 exec_paramcmd(act, i);
2136 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2137 if (key_ret) /* just quit the incremental command on return */
2139 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2143 g_string_truncate(short_keys, short_keys->len - 1);
2145 g_string_free (short_keys, TRUE);
2146 g_string_free (short_keys_inc, TRUE);
2150 exec_paramcmd(const Action *act, const guint i) {
2151 GString *parampart = g_string_new (uzbl.state.keycmd);
2152 GString *actionname = g_string_new ("");
2153 GString *actionparam = g_string_new ("");
2154 g_string_erase (parampart, 0, i+1);
2156 g_string_printf (actionname, act->name, parampart->str);
2158 g_string_printf (actionparam, act->param, parampart->str);
2159 parse_command(actionname->str, actionparam->str, NULL);
2160 g_string_free(actionname, TRUE);
2161 g_string_free(actionparam, TRUE);
2162 g_string_free(parampart, TRUE);
2170 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2171 //main_window_ref = g_object_ref(scrolled_window);
2172 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
2174 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2175 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2177 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2178 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2179 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2180 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2181 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2182 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2183 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2184 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2189 return scrolled_window;
2196 g->mainbar = gtk_hbox_new (FALSE, 0);
2198 /* keep a reference to the bar so we can re-pack it at runtime*/
2199 //sbar_ref = g_object_ref(g->mainbar);
2201 g->mainbar_label = gtk_label_new ("");
2202 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2203 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2204 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2205 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2206 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2207 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2213 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2214 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2215 gtk_widget_set_name (window, "Uzbl browser");
2216 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2217 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2218 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2225 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2226 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2227 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2234 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2236 If actname is one that calls an external command, this function will inject
2237 newargs in front of the user-provided args in that command line. They will
2238 come become after the body of the script (in sh) or after the name of
2239 the command to execute (in spawn).
2240 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2241 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2243 The return value consist of two strings: the action (sh, ...) and its args.
2245 If act is not one that calls an external command, then the given action merely
2248 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2249 /* Arrr! Here be memory leaks */
2250 gchar *actdup = g_strdup(actname);
2251 g_array_append_val(rets, actdup);
2253 if ((g_strcmp0(actname, "spawn") == 0) ||
2254 (g_strcmp0(actname, "sh") == 0) ||
2255 (g_strcmp0(actname, "sync_spawn") == 0) ||
2256 (g_strcmp0(actname, "sync_sh") == 0)) {
2258 GString *a = g_string_new("");
2259 gchar **spawnparts = split_quoted(origargs, FALSE);
2260 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2261 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2263 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2264 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2266 g_array_append_val(rets, a->str);
2267 g_string_free(a, FALSE);
2268 g_strfreev(spawnparts);
2270 gchar *origdup = g_strdup(origargs);
2271 g_array_append_val(rets, origdup);
2273 return (gchar**)g_array_free(rets, FALSE);
2277 run_handler (const gchar *act, const gchar *args) {
2278 /* Consider this code a temporary hack to make the handlers usable.
2279 In practice, all this splicing, injection, and reconstruction is
2280 inefficient, annoying and hard to manage. Potential pitfalls arise
2281 when the handler specific args 1) are not quoted (the handler
2282 callbacks should take care of this) 2) are quoted but interfere
2283 with the users' own quotation. A more ideal solution is
2284 to refactor parse_command so that it doesn't just take a string
2285 and execute it; rather than that, we should have a function which
2286 returns the argument vector parsed from the string. This vector
2287 could be modified (e.g. insert additional args into it) before
2288 passing it to the next function that actually executes it. Though
2289 it still isn't perfect for chain actions.. will reconsider & re-
2290 factor when I have the time. -duc */
2292 char **parts = g_strsplit(act, " ", 2);
2294 if (g_strcmp0(parts[0], "chain") == 0) {
2295 GString *newargs = g_string_new("");
2296 gchar **chainparts = split_quoted(parts[1], FALSE);
2298 /* for every argument in the chain, inject the handler args
2299 and make sure the new parts are wrapped in quotes */
2300 gchar **cp = chainparts;
2302 gchar *quotless = NULL;
2303 gchar **spliced_quotless = NULL; // sigh -_-;
2304 gchar **inpart = NULL;
2307 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2309 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2310 } else quotless = g_strdup(*cp);
2312 spliced_quotless = g_strsplit(quotless, " ", 2);
2313 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2314 g_strfreev(spliced_quotless);
2316 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2322 parse_command(parts[0], &(newargs->str[1]), NULL);
2323 g_string_free(newargs, TRUE);
2324 g_strfreev(chainparts);
2327 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2328 parse_command(inparts[0], inparts[1], NULL);
2336 add_binding (const gchar *key, const gchar *act) {
2337 char **parts = g_strsplit(act, " ", 2);
2344 if (uzbl.state.verbose)
2345 printf ("Binding %-10s : %s\n", key, act);
2346 action = new_action(parts[0], parts[1]);
2348 if (g_hash_table_remove (uzbl.bindings, key))
2349 g_warning ("Overwriting existing binding for \"%s\"", key);
2350 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2355 get_xdg_var (XDG_Var xdg) {
2356 const gchar* actual_value = getenv (xdg.environmental);
2357 const gchar* home = getenv ("HOME");
2358 gchar* return_value;
2360 if (! actual_value || strcmp (actual_value, "") == 0) {
2361 if (xdg.default_value) {
2362 return_value = str_replace ("~", home, xdg.default_value);
2364 return_value = NULL;
2367 return_value = str_replace("~", home, actual_value);
2370 return return_value;
2374 find_xdg_file (int xdg_type, char* filename) {
2375 /* xdg_type = 0 => config
2376 xdg_type = 1 => data
2377 xdg_type = 2 => cache*/
2379 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2380 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2383 gchar* temporary_string;
2387 if (! file_exists (temporary_file) && xdg_type != 2) {
2388 buf = get_xdg_var (XDG[3 + xdg_type]);
2389 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2392 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2393 g_free (temporary_file);
2394 temporary_file = g_strconcat (temporary_string, filename, NULL);
2398 //g_free (temporary_string); - segfaults.
2400 if (file_exists (temporary_file)) {
2401 return temporary_file;
2408 State *s = &uzbl.state;
2409 Network *n = &uzbl.net;
2411 for (i = 0; default_config[i].command != NULL; i++) {
2412 parse_cmd_line(default_config[i].command, NULL);
2415 if (g_strcmp0(s->config_file, "-") == 0) {
2416 s->config_file = NULL;
2420 else if (!s->config_file) {
2421 s->config_file = find_xdg_file (0, "/uzbl/config");
2424 if (s->config_file) {
2425 GArray* lines = read_file_by_line (s->config_file);
2429 while ((line = g_array_index(lines, gchar*, i))) {
2430 parse_cmd_line (line, NULL);
2434 g_array_free (lines, TRUE);
2436 if (uzbl.state.verbose)
2437 printf ("No configuration file loaded.\n");
2440 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2443 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2446 if (!uzbl.behave.cookie_handler)
2449 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2450 GString *s = g_string_new ("");
2451 SoupURI * soup_uri = soup_message_get_uri(msg);
2452 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2453 run_handler(uzbl.behave.cookie_handler, s->str);
2455 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2456 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2457 if ( p != NULL ) *p = '\0';
2458 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2460 if (uzbl.comm.sync_stdout)
2461 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2463 g_string_free(s, TRUE);
2467 save_cookies (SoupMessage *msg, gpointer user_data){
2471 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2472 cookie = soup_cookie_to_set_cookie_header(ck->data);
2473 SoupURI * soup_uri = soup_message_get_uri(msg);
2474 GString *s = g_string_new ("");
2475 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2476 run_handler(uzbl.behave.cookie_handler, s->str);
2478 g_string_free(s, TRUE);
2483 /* --- WEBINSPECTOR --- */
2485 hide_window_cb(GtkWidget *widget, gpointer data) {
2488 gtk_widget_hide(widget);
2492 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2495 (void) web_inspector;
2496 GtkWidget* scrolled_window;
2497 GtkWidget* new_web_view;
2500 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2501 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2502 G_CALLBACK(hide_window_cb), NULL);
2504 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2505 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2506 gtk_widget_show(g->inspector_window);
2508 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2509 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2510 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2511 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2512 gtk_widget_show(scrolled_window);
2514 new_web_view = webkit_web_view_new();
2515 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2517 return WEBKIT_WEB_VIEW(new_web_view);
2521 inspector_show_window_cb (WebKitWebInspector* inspector){
2523 gtk_widget_show(uzbl.gui.inspector_window);
2527 /* TODO: Add variables and code to make use of these functions */
2529 inspector_close_window_cb (WebKitWebInspector* inspector){
2535 inspector_attach_window_cb (WebKitWebInspector* inspector){
2541 inspector_detach_window_cb (WebKitWebInspector* inspector){
2547 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2553 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2559 set_up_inspector() {
2561 WebKitWebSettings *settings = view_settings();
2562 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2564 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2565 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2566 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2567 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2568 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2569 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2570 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2576 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2578 uzbl_cmdprop *c = v;
2583 if(c->type == TYPE_STR)
2584 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2585 else if(c->type == TYPE_INT)
2586 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2587 else if(c->type == TYPE_FLOAT)
2588 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2592 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2596 printf("bind %s = %s %s\n", (char *)k ,
2597 (char *)a->name, a->param?(char *)a->param:"");
2602 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2603 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2607 retreive_geometry() {
2609 GString *buf = g_string_new("");
2611 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2612 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2614 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2616 if(uzbl.gui.geometry)
2617 g_free(uzbl.gui.geometry);
2618 uzbl.gui.geometry = g_string_free(buf, FALSE);
2621 /* set up gtk, gobject, variable defaults and other things that tests and other
2622 * external applications need to do anyhow */
2624 initialize(int argc, char *argv[]) {
2625 gtk_init (&argc, &argv);
2626 if (!g_thread_supported ())
2627 g_thread_init (NULL);
2628 uzbl.state.executable_path = g_strdup(argv[0]);
2629 uzbl.state.selected_url = NULL;
2630 uzbl.state.searchtx = NULL;
2632 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2633 g_option_context_add_main_entries (context, entries, NULL);
2634 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2635 g_option_context_parse (context, &argc, &argv, NULL);
2636 g_option_context_free(context);
2638 if (uzbl.behave.print_version) {
2639 printf("Commit: %s\n", COMMIT);
2643 /* initialize hash table */
2644 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2646 uzbl.net.soup_session = webkit_get_default_session();
2647 uzbl.state.keycmd = g_strdup("");
2649 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2650 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2651 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2652 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2653 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2654 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2656 uzbl.gui.sbar.progress_s = g_strdup("=");
2657 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2658 uzbl.gui.sbar.progress_w = 10;
2660 /* HTML mode defaults*/
2661 uzbl.behave.html_buffer = g_string_new("");
2662 uzbl.behave.html_endmarker = g_strdup(".");
2663 uzbl.behave.html_timeout = 60;
2664 uzbl.behave.base_url = g_strdup("http://invalid");
2666 /* default mode indicators */
2667 uzbl.behave.insert_indicator = g_strdup("I");
2668 uzbl.behave.cmd_indicator = g_strdup("C");
2670 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2671 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2672 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2673 uzbl.info.arch = ARCH;
2674 uzbl.info.commit = COMMIT;
2677 make_var_to_name_hash();
2679 uzbl.gui.scrolled_win = create_browser();
2682 #ifndef UZBL_LIBRARY
2685 main (int argc, char* argv[]) {
2686 initialize(argc, argv);
2688 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2692 /* initial packing */
2693 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2694 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2696 if (uzbl.state.socket_id) {
2697 uzbl.gui.plug = create_plug ();
2698 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2699 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2701 uzbl.gui.main_window = create_window ();
2702 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2703 gtk_widget_show_all (uzbl.gui.main_window);
2704 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2707 if(!uzbl.state.instance_name)
2708 uzbl.state.instance_name = itos((int)uzbl.xwin);
2710 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2712 if (uzbl.state.verbose) {
2713 printf("Uzbl start location: %s\n", argv[0]);
2714 if (uzbl.state.socket_id)
2715 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2717 printf("window_id %i\n",(int) uzbl.xwin);
2718 printf("pid %i\n", getpid ());
2719 printf("name: %s\n", uzbl.state.instance_name);
2722 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2723 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2724 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2725 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2726 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2728 if(uzbl.gui.geometry)
2731 retreive_geometry();
2733 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2734 gboolean verbose_override = uzbl.state.verbose;
2737 set_insert_mode(FALSE);
2739 if (!uzbl.behave.show_status)
2740 gtk_widget_hide(uzbl.gui.mainbar);
2747 if (verbose_override > uzbl.state.verbose)
2748 uzbl.state.verbose = verbose_override;
2751 set_var_value("uri", uri_override);
2752 g_free(uri_override);
2753 } else if (uzbl.state.uri)
2754 cmd_load_uri(uzbl.gui.web_view, NULL);
2759 return EXIT_SUCCESS;
2763 /* vi: set et ts=4: */