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 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "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 "Path to config file or '-' for stdin", "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 */
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_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;
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);
482 /* used for html_mode_timeout
483 * be sure to extend this function to use
484 * more timers if needed in other places
487 set_timeout(int seconds) {
489 memset(&t, 0, sizeof t);
491 t.it_value.tv_sec = seconds;
492 t.it_value.tv_usec = 0;
493 setitimer(ITIMER_REAL, &t, NULL);
496 /* --- SIGNAL HANDLER --- */
499 catch_sigterm(int s) {
505 catch_sigint(int s) {
515 set_var_value("mode", "0");
520 /* --- CALLBACKS --- */
523 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
526 (void) navigation_action;
527 (void) policy_decision;
529 const gchar* uri = webkit_network_request_get_uri (request);
530 if (uzbl.state.verbose)
531 printf("New window requested -> %s \n", uri);
532 webkit_web_policy_decision_use(policy_decision);
537 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
542 /* If we can display it, let's display it... */
543 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
544 webkit_web_policy_decision_use (policy_decision);
548 /* ...everything we can't displayed is downloaded */
549 webkit_web_policy_decision_download (policy_decision);
554 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
558 if (uzbl.state.selected_url != NULL) {
559 if (uzbl.state.verbose)
560 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
561 new_window_load_uri(uzbl.state.selected_url);
563 if (uzbl.state.verbose)
564 printf("New web view -> %s\n","Nothing to open, exiting");
570 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
573 if (uzbl.behave.download_handler) {
574 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
575 if (uzbl.state.verbose)
576 printf("Download -> %s\n",uri);
577 /* if urls not escaped, we may have to escape and quote uri before this call */
578 run_handler(uzbl.behave.download_handler, uri);
583 /* scroll a bar in a given direction */
585 scroll (GtkAdjustment* bar, GArray *argv) {
589 gdouble page_size = gtk_adjustment_get_page_size(bar);
590 gdouble value = gtk_adjustment_get_value(bar);
591 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
594 value += page_size * amount * 0.01;
598 max_value = gtk_adjustment_get_upper(bar) - page_size;
600 if (value > max_value)
601 value = max_value; /* don't scroll past the end of the page */
603 gtk_adjustment_set_value (bar, value);
607 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
608 (void) page; (void) argv; (void) result;
609 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
613 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
614 (void) page; (void) argv; (void) result;
615 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
616 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
620 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
621 (void) page; (void) result;
622 scroll(uzbl.gui.bar_v, argv);
626 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
627 (void) page; (void) result;
628 scroll(uzbl.gui.bar_h, argv);
633 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
634 if(uzbl.state.verbose)
635 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
637 /* update geometry var with the actual geometry
638 this is necessary as some WMs don't seem to honour
639 the above setting and we don't want to end up with
640 wrong geometry information
647 if (!uzbl.behave.show_status) {
648 gtk_widget_hide(uzbl.gui.mainbar);
650 gtk_widget_show(uzbl.gui.mainbar);
656 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
661 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
665 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
670 if (uzbl.behave.show_status) {
671 gtk_widget_hide(uzbl.gui.mainbar);
673 gtk_widget_show(uzbl.gui.mainbar);
675 uzbl.behave.show_status = !uzbl.behave.show_status;
680 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
684 //Set selected_url state variable
685 g_free(uzbl.state.selected_url);
686 uzbl.state.selected_url = NULL;
688 uzbl.state.selected_url = g_strdup(link);
694 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
697 const gchar *title = webkit_web_view_get_title(web_view);
698 if (uzbl.gui.main_title)
699 g_free (uzbl.gui.main_title);
700 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
705 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
708 uzbl.gui.sbar.load_progress = progress;
710 g_free(uzbl.gui.sbar.progress_bar);
711 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
717 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
721 if (uzbl.behave.load_finish_handler)
722 run_handler(uzbl.behave.load_finish_handler, "");
725 void clear_keycmd() {
726 g_free(uzbl.state.keycmd);
727 uzbl.state.keycmd = g_strdup("");
731 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
735 uzbl.gui.sbar.load_progress = 0;
736 clear_keycmd(); // don't need old commands to remain on new page?
737 if (uzbl.behave.load_start_handler)
738 run_handler(uzbl.behave.load_start_handler, "");
742 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
745 g_free (uzbl.state.uri);
746 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
747 uzbl.state.uri = g_string_free (newuri, FALSE);
748 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
749 set_insert_mode(uzbl.behave.always_insert_mode);
752 if (uzbl.behave.load_commit_handler)
753 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
757 destroy_cb (GtkWidget* widget, gpointer data) {
765 if (uzbl.behave.history_handler) {
767 struct tm * timeinfo;
770 timeinfo = localtime ( &rawtime );
771 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
772 run_handler(uzbl.behave.history_handler, date);
777 /* VIEW funcs (little webkit wrappers) */
778 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
780 VIEWFUNC(reload_bypass_cache)
781 VIEWFUNC(stop_loading)
788 /* -- command to callback/function map for things we cannot attach to any signals */
789 struct {char *key; CommandInfo value;} cmdlist[] =
790 { /* key function no_split */
791 { "back", {view_go_back, 0} },
792 { "forward", {view_go_forward, 0} },
793 { "scroll_vert", {scroll_vert, 0} },
794 { "scroll_horz", {scroll_horz, 0} },
795 { "scroll_begin", {scroll_begin, 0} },
796 { "scroll_end", {scroll_end, 0} },
797 { "reload", {view_reload, 0}, },
798 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
799 { "stop", {view_stop_loading, 0}, },
800 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
801 { "zoom_out", {view_zoom_out, 0}, },
802 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
803 { "uri", {load_uri, TRUE} },
804 { "js", {run_js, TRUE} },
805 { "script", {run_external_js, 0} },
806 { "toggle_status", {toggle_status_cb, 0} },
807 { "spawn", {spawn, 0} },
808 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
809 { "sh", {spawn_sh, 0} },
810 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
811 { "exit", {close_uzbl, 0} },
812 { "search", {search_forward_text, TRUE} },
813 { "search_reverse", {search_reverse_text, TRUE} },
814 { "dehilight", {dehilight, 0} },
815 { "toggle_insert_mode", {toggle_insert_mode, 0} },
816 { "set", {set_var, TRUE} },
817 //{ "get", {get_var, TRUE} },
818 { "bind", {act_bind, TRUE} },
819 { "dump_config", {act_dump_config, 0} },
820 { "keycmd", {keycmd, TRUE} },
821 { "keycmd_nl", {keycmd_nl, TRUE} },
822 { "keycmd_bs", {keycmd_bs, 0} },
823 { "chain", {chain, 0} },
824 { "print", {print, TRUE} }
831 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
833 for (i = 0; i < LENGTH(cmdlist); i++)
834 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
837 /* -- CORE FUNCTIONS -- */
840 free_action(gpointer act) {
841 Action *action = (Action*)act;
842 g_free(action->name);
844 g_free(action->param);
849 new_action(const gchar *name, const gchar *param) {
850 Action *action = g_new(Action, 1);
852 action->name = g_strdup(name);
854 action->param = g_strdup(param);
856 action->param = NULL;
862 file_exists (const char * filename) {
863 return (access(filename, F_OK) == 0);
867 set_var(WebKitWebView *page, GArray *argv, GString *result) {
868 (void) page; (void) result;
869 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
870 if (split[0] != NULL) {
871 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
872 set_var_value(g_strstrip(split[0]), value);
879 print(WebKitWebView *page, GArray *argv, GString *result) {
880 (void) page; (void) result;
883 buf = expand(argv_idx(argv, 0), 0);
884 g_string_assign(result, buf);
889 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
890 (void) page; (void) result;
891 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
892 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
893 add_binding(g_strstrip(split[0]), value);
911 set_mode_indicator() {
912 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
913 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
918 set_mode_indicator();
923 set_insert_mode(gboolean mode) {
924 uzbl.behave.insert_mode = mode;
925 set_mode_indicator();
929 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
930 (void) page; (void) result;
932 if (argv_idx(argv, 0)) {
933 if (strcmp (argv_idx(argv, 0), "0") == 0) {
934 set_insert_mode(FALSE);
936 set_insert_mode(TRUE);
939 set_insert_mode( !uzbl.behave.insert_mode );
946 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
949 if (argv_idx(argv, 0)) {
950 GString* newuri = g_string_new (argv_idx(argv, 0));
951 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
952 run_js(web_view, argv, NULL);
955 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
956 g_string_prepend (newuri, "http://");
957 /* if we do handle cookies, ask our handler for them */
958 webkit_web_view_load_uri (web_view, newuri->str);
959 g_string_free (newuri, TRUE);
966 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
967 size_t argumentCount, const JSValueRef arguments[],
968 JSValueRef* exception) {
973 JSStringRef js_result_string;
974 GString *result = g_string_new("");
976 if (argumentCount >= 1) {
977 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
978 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
979 char ctl_line[arg_size];
980 JSStringGetUTF8CString(arg, ctl_line, arg_size);
982 parse_cmd_line(ctl_line, result);
984 JSStringRelease(arg);
986 js_result_string = JSStringCreateWithUTF8CString(result->str);
988 g_string_free(result, TRUE);
990 return JSValueMakeString(ctx, js_result_string);
993 JSStaticFunction js_static_functions[] = {
994 {"run", js_run_command, kJSPropertyAttributeNone},
999 /* This function creates the class and its definition, only once */
1000 if (!uzbl.js.initialized) {
1001 /* it would be pretty cool to make this dynamic */
1002 uzbl.js.classdef = kJSClassDefinitionEmpty;
1003 uzbl.js.classdef.staticFunctions = js_static_functions;
1005 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1011 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1012 WebKitWebFrame *frame;
1013 JSGlobalContextRef context;
1014 JSObjectRef globalobject;
1015 JSStringRef var_name;
1017 JSStringRef js_script;
1018 JSValueRef js_result;
1019 JSStringRef js_result_string;
1020 size_t js_result_size;
1024 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1025 context = webkit_web_frame_get_global_context(frame);
1026 globalobject = JSContextGetGlobalObject(context);
1028 /* uzbl javascript namespace */
1029 var_name = JSStringCreateWithUTF8CString("Uzbl");
1030 JSObjectSetProperty(context, globalobject, var_name,
1031 JSObjectMake(context, uzbl.js.classref, NULL),
1032 kJSClassAttributeNone, NULL);
1034 /* evaluate the script and get return value*/
1035 js_script = JSStringCreateWithUTF8CString(script);
1036 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1037 if (js_result && !JSValueIsUndefined(context, js_result)) {
1038 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1039 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1041 if (js_result_size) {
1042 char js_result_utf8[js_result_size];
1043 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1044 g_string_assign(result, js_result_utf8);
1047 JSStringRelease(js_result_string);
1051 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1053 JSStringRelease(var_name);
1054 JSStringRelease(js_script);
1058 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1059 if (argv_idx(argv, 0))
1060 eval_js(web_view, argv_idx(argv, 0), result);
1064 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1066 if (argv_idx(argv, 0)) {
1067 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1072 while ((line = g_array_index(lines, gchar*, i))) {
1074 js = g_strdup (line);
1076 gchar* newjs = g_strconcat (js, line, NULL);
1083 if (uzbl.state.verbose)
1084 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1086 if (argv_idx (argv, 1)) {
1087 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1091 eval_js (web_view, js, result);
1093 g_array_free (lines, TRUE);
1098 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1099 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1100 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1101 webkit_web_view_unmark_text_matches (page);
1102 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1103 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1107 if (uzbl.state.searchtx) {
1108 if (uzbl.state.verbose)
1109 printf ("Searching: %s\n", uzbl.state.searchtx);
1110 webkit_web_view_set_highlight_text_matches (page, TRUE);
1111 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1116 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1118 search_text(page, argv, TRUE);
1122 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1124 search_text(page, argv, FALSE);
1128 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1129 (void) argv; (void) result;
1130 webkit_web_view_set_highlight_text_matches (page, FALSE);
1135 new_window_load_uri (const gchar * uri) {
1136 if (uzbl.behave.new_window) {
1137 GString *s = g_string_new ("");
1138 g_string_printf(s, "'%s'", uri);
1139 run_handler(uzbl.behave.new_window, s->str);
1142 GString* to_execute = g_string_new ("");
1143 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1145 for (i = 0; entries[i].long_name != NULL; i++) {
1146 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1147 gchar** str = (gchar**)entries[i].arg_data;
1149 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1153 if (uzbl.state.verbose)
1154 printf("\n%s\n", to_execute->str);
1155 g_spawn_command_line_async (to_execute->str, NULL);
1156 g_string_free (to_execute, TRUE);
1160 chain (WebKitWebView *page, GArray *argv, GString *result) {
1161 (void) page; (void) result;
1163 gchar **parts = NULL;
1165 while ((a = argv_idx(argv, i++))) {
1166 parts = g_strsplit (a, " ", 2);
1168 parse_command(parts[0], parts[1], result);
1174 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1178 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1184 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1188 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1194 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1199 int len = strlen(uzbl.state.keycmd);
1200 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1202 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1207 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1214 /* --Statusbar functions-- */
1216 build_progressbar_ascii(int percent) {
1217 int width=uzbl.gui.sbar.progress_w;
1220 GString *bar = g_string_new("");
1222 l = (double)percent*((double)width/100.);
1223 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1225 for(i=0; i<(int)l; i++)
1226 g_string_append(bar, uzbl.gui.sbar.progress_s);
1229 g_string_append(bar, uzbl.gui.sbar.progress_u);
1231 return g_string_free(bar, FALSE);
1233 /* --End Statusbar functions-- */
1236 sharg_append(GArray *a, const gchar *str) {
1237 const gchar *s = (str ? str : "");
1238 g_array_append_val(a, s);
1241 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1243 run_command (const gchar *command, const guint npre, const gchar **args,
1244 const gboolean sync, char **output_stdout) {
1245 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1248 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1249 gchar *pid = itos(getpid());
1250 gchar *xwin = itos(uzbl.xwin);
1252 sharg_append(a, command);
1253 for (i = 0; i < npre; i++) /* add n args before the default vars */
1254 sharg_append(a, args[i]);
1255 sharg_append(a, uzbl.state.config_file);
1256 sharg_append(a, pid);
1257 sharg_append(a, xwin);
1258 sharg_append(a, uzbl.comm.fifo_path);
1259 sharg_append(a, uzbl.comm.socket_path);
1260 sharg_append(a, uzbl.state.uri);
1261 sharg_append(a, uzbl.gui.main_title);
1263 for (i = npre; i < g_strv_length((gchar**)args); i++)
1264 sharg_append(a, args[i]);
1268 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1270 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1271 NULL, NULL, output_stdout, NULL, NULL, &err);
1272 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1273 NULL, NULL, NULL, &err);
1275 if (uzbl.state.verbose) {
1276 GString *s = g_string_new("spawned:");
1277 for (i = 0; i < (a->len); i++) {
1278 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1279 g_string_append_printf(s, " %s", qarg);
1282 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1283 printf("%s\n", s->str);
1284 g_string_free(s, TRUE);
1286 printf("Stdout: %s\n", *output_stdout);
1290 g_printerr("error on run_command: %s\n", err->message);
1295 g_array_free (a, TRUE);
1300 split_quoted(const gchar* src, const gboolean unquote) {
1301 /* split on unquoted space, return array of strings;
1302 remove a layer of quotes and backslashes if unquote */
1303 if (!src) return NULL;
1305 gboolean dq = FALSE;
1306 gboolean sq = FALSE;
1307 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1308 GString *s = g_string_new ("");
1312 for (p = src; *p != '\0'; p++) {
1313 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1314 else if (*p == '\\') { g_string_append_c(s, *p++);
1315 g_string_append_c(s, *p); }
1316 else if ((*p == '"') && unquote && !sq) dq = !dq;
1317 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1319 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1320 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1322 else if ((*p == ' ') && !dq && !sq) {
1323 dup = g_strdup(s->str);
1324 g_array_append_val(a, dup);
1325 g_string_truncate(s, 0);
1326 } else g_string_append_c(s, *p);
1328 dup = g_strdup(s->str);
1329 g_array_append_val(a, dup);
1330 ret = (gchar**)a->data;
1331 g_array_free (a, FALSE);
1332 g_string_free (s, TRUE);
1337 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1338 (void)web_view; (void)result;
1339 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1340 if (argv_idx(argv, 0))
1341 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1345 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1346 (void)web_view; (void)result;
1348 if (argv_idx(argv, 0))
1349 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1350 TRUE, &uzbl.comm.sync_stdout);
1354 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1355 (void)web_view; (void)result;
1356 if (!uzbl.behave.shell_cmd) {
1357 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1362 gchar *spacer = g_strdup("");
1363 g_array_insert_val(argv, 1, spacer);
1364 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1366 for (i = 1; i < g_strv_length(cmd); i++)
1367 g_array_prepend_val(argv, cmd[i]);
1369 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1375 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1376 (void)web_view; (void)result;
1377 if (!uzbl.behave.shell_cmd) {
1378 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1383 gchar *spacer = g_strdup("");
1384 g_array_insert_val(argv, 1, spacer);
1385 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1387 for (i = 1; i < g_strv_length(cmd); i++)
1388 g_array_prepend_val(argv, cmd[i]);
1390 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1391 TRUE, &uzbl.comm.sync_stdout);
1397 parse_command(const char *cmd, const char *param, GString *result) {
1400 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1402 gchar **par = split_quoted(param, TRUE);
1403 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1405 if (c->no_split) { /* don't split */
1406 sharg_append(a, param);
1408 for (i = 0; i < g_strv_length(par); i++)
1409 sharg_append(a, par[i]);
1412 if (result == NULL) {
1413 GString *result_print = g_string_new("");
1415 c->function(uzbl.gui.web_view, a, result_print);
1416 if (result_print->len)
1417 printf("%*s\n", result_print->len, result_print->str);
1419 g_string_free(result_print, TRUE);
1421 c->function(uzbl.gui.web_view, a, result);
1424 g_array_free (a, TRUE);
1427 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1434 if(*uzbl.net.proxy_url == ' '
1435 || uzbl.net.proxy_url == NULL) {
1436 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1437 (GType) SOUP_SESSION_PROXY_URI);
1440 suri = soup_uri_new(uzbl.net.proxy_url);
1441 g_object_set(G_OBJECT(uzbl.net.soup_session),
1442 SOUP_SESSION_PROXY_URI,
1444 soup_uri_free(suri);
1451 if(file_exists(uzbl.gui.icon)) {
1452 if (uzbl.gui.main_window)
1453 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1455 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1461 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1462 g_array_append_val (a, uzbl.state.uri);
1463 load_uri(uzbl.gui.web_view, a, NULL);
1464 g_array_free (a, TRUE);
1468 cmd_always_insert_mode() {
1469 set_insert_mode(uzbl.behave.always_insert_mode);
1475 g_object_set(G_OBJECT(uzbl.net.soup_session),
1476 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1480 cmd_max_conns_host() {
1481 g_object_set(G_OBJECT(uzbl.net.soup_session),
1482 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1487 soup_session_remove_feature
1488 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1489 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1490 /*g_free(uzbl.net.soup_logger);*/
1492 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1493 soup_session_add_feature(uzbl.net.soup_session,
1494 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1499 return webkit_web_view_get_settings(uzbl.gui.web_view);
1504 WebKitWebSettings *ws = view_settings();
1505 if (uzbl.behave.font_size > 0) {
1506 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1509 if (uzbl.behave.monospace_size > 0) {
1510 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1511 uzbl.behave.monospace_size, NULL);
1513 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1514 uzbl.behave.font_size, NULL);
1520 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1524 cmd_disable_plugins() {
1525 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1526 !uzbl.behave.disable_plugins, NULL);
1530 cmd_disable_scripts() {
1531 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1532 !uzbl.behave.disable_scripts, NULL);
1536 cmd_minimum_font_size() {
1537 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1538 uzbl.behave.minimum_font_size, NULL);
1541 cmd_autoload_img() {
1542 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1543 uzbl.behave.autoload_img, NULL);
1548 cmd_autoshrink_img() {
1549 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1550 uzbl.behave.autoshrink_img, NULL);
1555 cmd_enable_spellcheck() {
1556 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1557 uzbl.behave.enable_spellcheck, NULL);
1561 cmd_enable_private() {
1562 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1563 uzbl.behave.enable_private, NULL);
1568 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1569 uzbl.behave.print_bg, NULL);
1574 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1575 uzbl.behave.style_uri, NULL);
1579 cmd_resizable_txt() {
1580 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1581 uzbl.behave.resizable_txt, NULL);
1585 cmd_default_encoding() {
1586 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1587 uzbl.behave.default_encoding, NULL);
1591 cmd_enforce_96dpi() {
1592 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1593 uzbl.behave.enforce_96dpi, NULL);
1597 cmd_caret_browsing() {
1598 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1599 uzbl.behave.caret_browsing, NULL);
1603 cmd_cookie_handler() {
1604 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1605 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1606 if ((g_strcmp0(split[0], "sh") == 0) ||
1607 (g_strcmp0(split[0], "spawn") == 0)) {
1608 g_free (uzbl.behave.cookie_handler);
1609 uzbl.behave.cookie_handler =
1610 g_strdup_printf("sync_%s %s", split[0], split[1]);
1617 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1618 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1619 if ((g_strcmp0(split[0], "sh") == 0) ||
1620 (g_strcmp0(split[0], "spawn") == 0)) {
1621 g_free (uzbl.behave.new_window);
1622 uzbl.behave.new_window =
1623 g_strdup_printf("%s %s", split[0], split[1]);
1630 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1635 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1640 if(uzbl.behave.inject_html) {
1641 webkit_web_view_load_html_string (uzbl.gui.web_view,
1642 uzbl.behave.inject_html, NULL);
1651 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1652 uzbl.behave.modmask = 0;
1654 if(uzbl.behave.modkey)
1655 g_free(uzbl.behave.modkey);
1656 uzbl.behave.modkey = buf;
1658 for (i = 0; modkeys[i].key != NULL; i++) {
1659 if (g_strrstr(buf, modkeys[i].key))
1660 uzbl.behave.modmask |= modkeys[i].mask;
1666 if (*uzbl.net.useragent == ' ') {
1667 g_free (uzbl.net.useragent);
1668 uzbl.net.useragent = NULL;
1670 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1671 uzbl.net.useragent, NULL);
1677 if (!uzbl.gui.scrolled_win &&
1681 gtk_widget_ref(uzbl.gui.scrolled_win);
1682 gtk_widget_ref(uzbl.gui.mainbar);
1683 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1684 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1686 if(uzbl.behave.status_top) {
1687 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1688 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1691 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1692 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1694 gtk_widget_unref(uzbl.gui.scrolled_win);
1695 gtk_widget_unref(uzbl.gui.mainbar);
1696 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1701 set_var_value(gchar *name, gchar *val) {
1702 uzbl_cmdprop *c = NULL;
1705 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1707 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1708 if(!c->writeable) return FALSE;
1710 /* check for the variable type */
1711 if (c->type == TYPE_STR) {
1712 buf = expand(val, 0);
1713 if(*c->ptr) g_free(*c->ptr);
1715 } else if(c->type == TYPE_INT) {
1716 int *ip = (int *)c->ptr;
1717 buf = expand(val, 0);
1718 *ip = (int)strtoul(buf, &endp, 10);
1720 } else if (c->type == TYPE_FLOAT) {
1721 float *fp = (float *)c->ptr;
1722 buf = expand(val, 0);
1723 *fp = strtod(buf, &endp);
1727 /* invoke a command specific function */
1728 if(c->func) c->func();
1730 /* check wether name violates our naming scheme */
1731 if(strpbrk(name, invalid_chars)) {
1732 if (uzbl.state.verbose)
1733 printf("Invalid variable name\n");
1738 c = malloc(sizeof(uzbl_cmdprop));
1743 buf = expand(val, 0);
1744 c->ptr = malloc(sizeof(char *));
1746 g_hash_table_insert(uzbl.comm.proto_var,
1747 g_strdup(name), (gpointer) c);
1754 Behaviour *b = &uzbl.behave;
1756 if(b->html_buffer->str) {
1757 webkit_web_view_load_html_string (uzbl.gui.web_view,
1758 b->html_buffer->str, b->base_url);
1759 g_string_free(b->html_buffer, TRUE);
1760 b->html_buffer = g_string_new("");
1764 enum {M_CMD, M_HTML};
1766 parse_cmd_line(const char *ctl_line, GString *result) {
1767 Behaviour *b = &uzbl.behave;
1770 if(b->mode == M_HTML) {
1771 len = strlen(b->html_endmarker);
1772 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1773 if(len == strlen(ctl_line)-1 &&
1774 !strncmp(b->html_endmarker, ctl_line, len)) {
1776 set_var_value("mode", "0");
1781 set_timeout(b->html_timeout);
1782 g_string_append(b->html_buffer, ctl_line);
1785 else if((ctl_line[0] == '#') /* Comments */
1786 || (ctl_line[0] == ' ')
1787 || (ctl_line[0] == '\n'))
1788 ; /* ignore these lines */
1789 else { /* parse a command */
1791 gchar **tokens = NULL;
1792 len = strlen(ctl_line);
1794 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1795 ctlstrip = g_strndup(ctl_line, len - 1);
1796 else ctlstrip = g_strdup(ctl_line);
1798 tokens = g_strsplit(ctlstrip, " ", 2);
1799 parse_command(tokens[0], tokens[1], result);
1806 build_stream_name(int type, const gchar* dir) {
1807 State *s = &uzbl.state;
1811 str = g_strdup_printf
1812 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1813 } else if (type == SOCKET) {
1814 str = g_strdup_printf
1815 ("%s/uzbl_socket_%s", dir, s->instance_name);
1821 control_fifo(GIOChannel *gio, GIOCondition condition) {
1822 if (uzbl.state.verbose)
1823 printf("triggered\n");
1828 if (condition & G_IO_HUP)
1829 g_error ("Fifo: Read end of pipe died!\n");
1832 g_error ("Fifo: GIOChannel broke\n");
1834 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1835 if (ret == G_IO_STATUS_ERROR) {
1836 g_error ("Fifo: Error reading: %s\n", err->message);
1840 parse_cmd_line(ctl_line, NULL);
1847 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1848 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1849 if (unlink(uzbl.comm.fifo_path) == -1)
1850 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1851 g_free(uzbl.comm.fifo_path);
1852 uzbl.comm.fifo_path = NULL;
1855 GIOChannel *chan = NULL;
1856 GError *error = NULL;
1857 gchar *path = build_stream_name(FIFO, dir);
1859 if (!file_exists(path)) {
1860 if (mkfifo (path, 0666) == 0) {
1861 // 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.
1862 chan = g_io_channel_new_file(path, "r+", &error);
1864 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1865 if (uzbl.state.verbose)
1866 printf ("init_fifo: created successfully as %s\n", path);
1867 uzbl.comm.fifo_path = path;
1869 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1870 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1871 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1872 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1874 /* if we got this far, there was an error; cleanup */
1875 if (error) g_error_free (error);
1882 control_stdin(GIOChannel *gio, GIOCondition condition) {
1884 gchar *ctl_line = NULL;
1887 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1888 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1891 parse_cmd_line(ctl_line, NULL);
1899 GIOChannel *chan = NULL;
1900 GError *error = NULL;
1902 chan = g_io_channel_unix_new(fileno(stdin));
1904 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1905 g_error ("Stdin: could not add watch\n");
1907 if (uzbl.state.verbose)
1908 printf ("Stdin: watch added successfully\n");
1911 g_error ("Stdin: Error while opening: %s\n", error->message);
1913 if (error) g_error_free (error);
1917 control_socket(GIOChannel *chan) {
1918 struct sockaddr_un remote;
1919 unsigned int t = sizeof(remote);
1921 GIOChannel *clientchan;
1923 clientsock = accept (g_io_channel_unix_get_fd(chan),
1924 (struct sockaddr *) &remote, &t);
1926 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1927 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1928 (GIOFunc) control_client_socket, clientchan);
1935 control_client_socket(GIOChannel *clientchan) {
1937 GString *result = g_string_new("");
1938 GError *error = NULL;
1942 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1943 if (ret == G_IO_STATUS_ERROR) {
1944 g_warning ("Error reading: %s\n", error->message);
1945 g_io_channel_shutdown(clientchan, TRUE, &error);
1947 } else if (ret == G_IO_STATUS_EOF) {
1948 /* shutdown and remove channel watch from main loop */
1949 g_io_channel_shutdown(clientchan, TRUE, &error);
1954 parse_cmd_line (ctl_line, result);
1955 g_string_append_c(result, '\n');
1956 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1958 if (ret == G_IO_STATUS_ERROR) {
1959 g_warning ("Error writing: %s", error->message);
1961 g_io_channel_flush(clientchan, &error);
1964 if (error) g_error_free (error);
1965 g_string_free(result, TRUE);
1971 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1972 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1973 if (unlink(uzbl.comm.socket_path) == -1)
1974 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1975 g_free(uzbl.comm.socket_path);
1976 uzbl.comm.socket_path = NULL;
1984 GIOChannel *chan = NULL;
1986 struct sockaddr_un local;
1987 gchar *path = build_stream_name(SOCKET, dir);
1989 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1991 local.sun_family = AF_UNIX;
1992 strcpy (local.sun_path, path);
1993 unlink (local.sun_path);
1995 len = strlen (local.sun_path) + sizeof (local.sun_family);
1996 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1997 if (uzbl.state.verbose)
1998 printf ("init_socket: opened in %s\n", path);
2001 if( (chan = g_io_channel_unix_new(sock)) ) {
2002 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2003 uzbl.comm.socket_path = path;
2006 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2008 /* if we got this far, there was an error; cleanup */
2015 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2016 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2018 // this function may be called very early when the templates are not set (yet), hence the checks
2020 update_title (void) {
2021 Behaviour *b = &uzbl.behave;
2024 if (b->show_status) {
2025 if (b->title_format_short) {
2026 parsed = expand(b->title_format_short, 0);
2027 if (uzbl.gui.main_window)
2028 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2031 if (b->status_format) {
2032 parsed = expand(b->status_format, 0);
2033 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2036 if (b->status_background) {
2038 gdk_color_parse (b->status_background, &color);
2039 //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)
2040 if (uzbl.gui.main_window)
2041 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2042 else if (uzbl.gui.plug)
2043 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2046 if (b->title_format_long) {
2047 parsed = expand(b->title_format_long, 0);
2048 if (uzbl.gui.main_window)
2049 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2056 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2060 retrieve_geometry();
2065 key_press_cb (GtkWidget* window, GdkEventKey* event)
2067 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2071 if (event->type != GDK_KEY_PRESS ||
2072 event->keyval == GDK_Page_Up ||
2073 event->keyval == GDK_Page_Down ||
2074 event->keyval == GDK_Up ||
2075 event->keyval == GDK_Down ||
2076 event->keyval == GDK_Left ||
2077 event->keyval == GDK_Right ||
2078 event->keyval == GDK_Shift_L ||
2079 event->keyval == GDK_Shift_R)
2082 /* turn off insert mode (if always_insert_mode is not used) */
2083 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2084 set_insert_mode(uzbl.behave.always_insert_mode);
2089 if (uzbl.behave.insert_mode &&
2090 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2091 (!uzbl.behave.modmask)
2096 if (event->keyval == GDK_Escape) {
2099 dehilight(uzbl.gui.web_view, NULL, NULL);
2103 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2104 if (event->keyval == GDK_Insert) {
2106 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2107 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2109 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2112 GString* keycmd = g_string_new(uzbl.state.keycmd);
2113 g_string_append (keycmd, str);
2114 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2121 if (event->keyval == GDK_BackSpace)
2122 keycmd_bs(NULL, NULL, NULL);
2124 gboolean key_ret = FALSE;
2125 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2128 GString* keycmd = g_string_new(uzbl.state.keycmd);
2129 g_string_append(keycmd, event->string);
2130 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2133 run_keycmd(key_ret);
2135 if (key_ret) return (!uzbl.behave.insert_mode);
2140 run_keycmd(const gboolean key_ret) {
2141 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2143 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2145 parse_command(act->name, act->param, NULL);
2149 /* try if it's an incremental keycmd or one that takes args, and run it */
2150 GString* short_keys = g_string_new ("");
2151 GString* short_keys_inc = g_string_new ("");
2153 guint len = strlen(uzbl.state.keycmd);
2154 for (i=0; i<len; i++) {
2155 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2156 g_string_assign(short_keys_inc, short_keys->str);
2157 g_string_append_c(short_keys, '_');
2158 g_string_append_c(short_keys_inc, '*');
2160 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2161 /* run normal cmds only if return was pressed */
2162 exec_paramcmd(act, i);
2165 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2166 if (key_ret) /* just quit the incremental command on return */
2168 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2172 g_string_truncate(short_keys, short_keys->len - 1);
2174 g_string_free (short_keys, TRUE);
2175 g_string_free (short_keys_inc, TRUE);
2179 exec_paramcmd(const Action *act, const guint i) {
2180 GString *parampart = g_string_new (uzbl.state.keycmd);
2181 GString *actionname = g_string_new ("");
2182 GString *actionparam = g_string_new ("");
2183 g_string_erase (parampart, 0, i+1);
2185 g_string_printf (actionname, act->name, parampart->str);
2187 g_string_printf (actionparam, act->param, parampart->str);
2188 parse_command(actionname->str, actionparam->str, NULL);
2189 g_string_free(actionname, TRUE);
2190 g_string_free(actionparam, TRUE);
2191 g_string_free(parampart, TRUE);
2199 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2201 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2202 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2203 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2204 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2205 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2206 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2207 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2208 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2209 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2210 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2211 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2218 g->mainbar = gtk_hbox_new (FALSE, 0);
2220 /* keep a reference to the bar so we can re-pack it at runtime*/
2221 //sbar_ref = g_object_ref(g->mainbar);
2223 g->mainbar_label = gtk_label_new ("");
2224 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2225 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2226 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2227 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2228 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2229 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2235 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2236 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2237 gtk_widget_set_name (window, "Uzbl browser");
2238 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2239 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2240 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2247 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2248 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2249 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2256 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2258 If actname is one that calls an external command, this function will inject
2259 newargs in front of the user-provided args in that command line. They will
2260 come become after the body of the script (in sh) or after the name of
2261 the command to execute (in spawn).
2262 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2263 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2265 The return value consist of two strings: the action (sh, ...) and its args.
2267 If act is not one that calls an external command, then the given action merely
2270 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2271 /* Arrr! Here be memory leaks */
2272 gchar *actdup = g_strdup(actname);
2273 g_array_append_val(rets, actdup);
2275 if ((g_strcmp0(actname, "spawn") == 0) ||
2276 (g_strcmp0(actname, "sh") == 0) ||
2277 (g_strcmp0(actname, "sync_spawn") == 0) ||
2278 (g_strcmp0(actname, "sync_sh") == 0)) {
2280 GString *a = g_string_new("");
2281 gchar **spawnparts = split_quoted(origargs, FALSE);
2282 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2283 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2285 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2286 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2288 g_array_append_val(rets, a->str);
2289 g_string_free(a, FALSE);
2290 g_strfreev(spawnparts);
2292 gchar *origdup = g_strdup(origargs);
2293 g_array_append_val(rets, origdup);
2295 return (gchar**)g_array_free(rets, FALSE);
2299 run_handler (const gchar *act, const gchar *args) {
2300 /* Consider this code a temporary hack to make the handlers usable.
2301 In practice, all this splicing, injection, and reconstruction is
2302 inefficient, annoying and hard to manage. Potential pitfalls arise
2303 when the handler specific args 1) are not quoted (the handler
2304 callbacks should take care of this) 2) are quoted but interfere
2305 with the users' own quotation. A more ideal solution is
2306 to refactor parse_command so that it doesn't just take a string
2307 and execute it; rather than that, we should have a function which
2308 returns the argument vector parsed from the string. This vector
2309 could be modified (e.g. insert additional args into it) before
2310 passing it to the next function that actually executes it. Though
2311 it still isn't perfect for chain actions.. will reconsider & re-
2312 factor when I have the time. -duc */
2314 char **parts = g_strsplit(act, " ", 2);
2316 if (g_strcmp0(parts[0], "chain") == 0) {
2317 GString *newargs = g_string_new("");
2318 gchar **chainparts = split_quoted(parts[1], FALSE);
2320 /* for every argument in the chain, inject the handler args
2321 and make sure the new parts are wrapped in quotes */
2322 gchar **cp = chainparts;
2324 gchar *quotless = NULL;
2325 gchar **spliced_quotless = NULL; // sigh -_-;
2326 gchar **inpart = NULL;
2329 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2331 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2332 } else quotless = g_strdup(*cp);
2334 spliced_quotless = g_strsplit(quotless, " ", 2);
2335 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2336 g_strfreev(spliced_quotless);
2338 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2344 parse_command(parts[0], &(newargs->str[1]), NULL);
2345 g_string_free(newargs, TRUE);
2346 g_strfreev(chainparts);
2349 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2350 parse_command(inparts[0], inparts[1], NULL);
2358 add_binding (const gchar *key, const gchar *act) {
2359 char **parts = g_strsplit(act, " ", 2);
2366 if (uzbl.state.verbose)
2367 printf ("Binding %-10s : %s\n", key, act);
2368 action = new_action(parts[0], parts[1]);
2370 if (g_hash_table_remove (uzbl.bindings, key))
2371 g_warning ("Overwriting existing binding for \"%s\"", key);
2372 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2377 get_xdg_var (XDG_Var xdg) {
2378 const gchar* actual_value = getenv (xdg.environmental);
2379 const gchar* home = getenv ("HOME");
2380 gchar* return_value;
2382 if (! actual_value || strcmp (actual_value, "") == 0) {
2383 if (xdg.default_value) {
2384 return_value = str_replace ("~", home, xdg.default_value);
2386 return_value = NULL;
2389 return_value = str_replace("~", home, actual_value);
2392 return return_value;
2396 find_xdg_file (int xdg_type, char* filename) {
2397 /* xdg_type = 0 => config
2398 xdg_type = 1 => data
2399 xdg_type = 2 => cache*/
2401 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2402 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2405 gchar* temporary_string;
2409 if (! file_exists (temporary_file) && xdg_type != 2) {
2410 buf = get_xdg_var (XDG[3 + xdg_type]);
2411 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2414 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2415 g_free (temporary_file);
2416 temporary_file = g_strconcat (temporary_string, filename, NULL);
2420 //g_free (temporary_string); - segfaults.
2422 if (file_exists (temporary_file)) {
2423 return temporary_file;
2430 State *s = &uzbl.state;
2431 Network *n = &uzbl.net;
2433 for (i = 0; default_config[i].command != NULL; i++) {
2434 parse_cmd_line(default_config[i].command, NULL);
2437 if (g_strcmp0(s->config_file, "-") == 0) {
2438 s->config_file = NULL;
2442 else if (!s->config_file) {
2443 s->config_file = find_xdg_file (0, "/uzbl/config");
2446 if (s->config_file) {
2447 GArray* lines = read_file_by_line (s->config_file);
2451 while ((line = g_array_index(lines, gchar*, i))) {
2452 parse_cmd_line (line, NULL);
2456 g_array_free (lines, TRUE);
2458 if (uzbl.state.verbose)
2459 printf ("No configuration file loaded.\n");
2462 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2465 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2468 if (!uzbl.behave.cookie_handler)
2471 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2472 GString *s = g_string_new ("");
2473 SoupURI * soup_uri = soup_message_get_uri(msg);
2474 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2475 run_handler(uzbl.behave.cookie_handler, s->str);
2477 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2478 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2479 if ( p != NULL ) *p = '\0';
2480 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2482 if (uzbl.comm.sync_stdout)
2483 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2485 g_string_free(s, TRUE);
2489 save_cookies (SoupMessage *msg, gpointer user_data){
2493 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2494 cookie = soup_cookie_to_set_cookie_header(ck->data);
2495 SoupURI * soup_uri = soup_message_get_uri(msg);
2496 GString *s = g_string_new ("");
2497 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2498 run_handler(uzbl.behave.cookie_handler, s->str);
2500 g_string_free(s, TRUE);
2505 /* --- WEBINSPECTOR --- */
2507 hide_window_cb(GtkWidget *widget, gpointer data) {
2510 gtk_widget_hide(widget);
2514 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2517 (void) web_inspector;
2518 GtkWidget* scrolled_window;
2519 GtkWidget* new_web_view;
2522 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2523 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2524 G_CALLBACK(hide_window_cb), NULL);
2526 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2527 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2528 gtk_widget_show(g->inspector_window);
2530 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2531 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2532 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2533 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2534 gtk_widget_show(scrolled_window);
2536 new_web_view = webkit_web_view_new();
2537 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2539 return WEBKIT_WEB_VIEW(new_web_view);
2543 inspector_show_window_cb (WebKitWebInspector* inspector){
2545 gtk_widget_show(uzbl.gui.inspector_window);
2549 /* TODO: Add variables and code to make use of these functions */
2551 inspector_close_window_cb (WebKitWebInspector* inspector){
2557 inspector_attach_window_cb (WebKitWebInspector* inspector){
2563 inspector_detach_window_cb (WebKitWebInspector* inspector){
2569 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2575 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2581 set_up_inspector() {
2583 WebKitWebSettings *settings = view_settings();
2584 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2586 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2587 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2588 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2589 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2590 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2591 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2592 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2594 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2598 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2600 uzbl_cmdprop *c = v;
2605 if(c->type == TYPE_STR)
2606 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2607 else if(c->type == TYPE_INT)
2608 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2609 else if(c->type == TYPE_FLOAT)
2610 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2614 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2618 printf("bind %s = %s %s\n", (char *)k ,
2619 (char *)a->name, a->param?(char *)a->param:"");
2624 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2625 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2629 retrieve_geometry() {
2631 GString *buf = g_string_new("");
2633 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2634 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2636 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2638 if(uzbl.gui.geometry)
2639 g_free(uzbl.gui.geometry);
2640 uzbl.gui.geometry = g_string_free(buf, FALSE);
2643 /* set up gtk, gobject, variable defaults and other things that tests and other
2644 * external applications need to do anyhow */
2646 initialize(int argc, char *argv[]) {
2647 if (!g_thread_supported ())
2648 g_thread_init (NULL);
2649 uzbl.state.executable_path = g_strdup(argv[0]);
2650 uzbl.state.selected_url = NULL;
2651 uzbl.state.searchtx = NULL;
2653 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2654 g_option_context_add_main_entries (context, entries, NULL);
2655 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2656 g_option_context_parse (context, &argc, &argv, NULL);
2657 g_option_context_free(context);
2659 if (uzbl.behave.print_version) {
2660 printf("Commit: %s\n", COMMIT);
2664 /* initialize hash table */
2665 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2667 uzbl.net.soup_session = webkit_get_default_session();
2668 uzbl.state.keycmd = g_strdup("");
2670 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2671 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2672 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2673 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2674 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2675 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2677 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2678 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2679 uzbl.gui.sbar.progress_w = 10;
2681 /* HTML mode defaults*/
2682 uzbl.behave.html_buffer = g_string_new("");
2683 uzbl.behave.html_endmarker = g_strdup(".");
2684 uzbl.behave.html_timeout = 60;
2685 uzbl.behave.base_url = g_strdup("http://invalid");
2687 /* default mode indicators */
2688 uzbl.behave.insert_indicator = g_strdup("I");
2689 uzbl.behave.cmd_indicator = g_strdup("C");
2691 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2692 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2693 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2694 uzbl.info.arch = ARCH;
2695 uzbl.info.commit = COMMIT;
2698 make_var_to_name_hash();
2703 #ifndef UZBL_LIBRARY
2706 main (int argc, char* argv[]) {
2707 initialize(argc, argv);
2709 gtk_init (&argc, &argv);
2711 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2712 //main_window_ref = g_object_ref(scrolled_window);
2713 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2714 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2716 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2717 GTK_WIDGET (uzbl.gui.web_view));
2719 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2723 /* initial packing */
2724 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2725 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2727 if (uzbl.state.socket_id) {
2728 uzbl.gui.plug = create_plug ();
2729 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2730 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2732 uzbl.gui.main_window = create_window ();
2733 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2734 gtk_widget_show_all (uzbl.gui.main_window);
2735 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2738 if(!uzbl.state.instance_name)
2739 uzbl.state.instance_name = itos((int)uzbl.xwin);
2741 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2743 if (uzbl.state.verbose) {
2744 printf("Uzbl start location: %s\n", argv[0]);
2745 if (uzbl.state.socket_id)
2746 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2748 printf("window_id %i\n",(int) uzbl.xwin);
2749 printf("pid %i\n", getpid ());
2750 printf("name: %s\n", uzbl.state.instance_name);
2753 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2754 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2755 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2756 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2757 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2759 if(uzbl.gui.geometry)
2762 retrieve_geometry();
2764 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2765 if (argc > 1 && !uzbl.state.uri)
2766 uri_override = g_strdup(argv[1]);
2767 gboolean verbose_override = uzbl.state.verbose;
2770 set_insert_mode(FALSE);
2772 if (!uzbl.behave.show_status)
2773 gtk_widget_hide(uzbl.gui.mainbar);
2780 if (verbose_override > uzbl.state.verbose)
2781 uzbl.state.verbose = verbose_override;
2784 set_var_value("uri", uri_override);
2785 g_free(uri_override);
2786 } else if (uzbl.state.uri)
2787 cmd_load_uri(uzbl.gui.web_view, NULL);
2792 return EXIT_SUCCESS;
2796 /* vi: set et ts=4: */