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} },
825 { "update_gui", {update_gui, 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 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) argv; (void) result;
887 print(WebKitWebView *page, GArray *argv, GString *result) {
888 (void) page; (void) result;
891 buf = expand(argv_idx(argv, 0), 0);
892 g_string_assign(result, buf);
897 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
898 (void) page; (void) result;
899 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
900 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
901 add_binding(g_strstrip(split[0]), value);
919 set_mode_indicator() {
920 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
921 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
926 set_mode_indicator();
931 set_insert_mode(gboolean mode) {
932 uzbl.behave.insert_mode = mode;
933 set_mode_indicator();
937 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
938 (void) page; (void) result;
940 if (argv_idx(argv, 0)) {
941 if (strcmp (argv_idx(argv, 0), "0") == 0) {
942 set_insert_mode(FALSE);
944 set_insert_mode(TRUE);
947 set_insert_mode( !uzbl.behave.insert_mode );
954 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
957 if (argv_idx(argv, 0)) {
958 GString* newuri = g_string_new (argv_idx(argv, 0));
959 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
960 run_js(web_view, argv, NULL);
963 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
964 g_string_prepend (newuri, "http://");
965 /* if we do handle cookies, ask our handler for them */
966 webkit_web_view_load_uri (web_view, newuri->str);
967 g_string_free (newuri, TRUE);
974 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
975 size_t argumentCount, const JSValueRef arguments[],
976 JSValueRef* exception) {
981 JSStringRef js_result_string;
982 GString *result = g_string_new("");
984 if (argumentCount >= 1) {
985 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
986 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
987 char ctl_line[arg_size];
988 JSStringGetUTF8CString(arg, ctl_line, arg_size);
990 parse_cmd_line(ctl_line, result);
992 JSStringRelease(arg);
994 js_result_string = JSStringCreateWithUTF8CString(result->str);
996 g_string_free(result, TRUE);
998 return JSValueMakeString(ctx, js_result_string);
1001 JSStaticFunction js_static_functions[] = {
1002 {"run", js_run_command, kJSPropertyAttributeNone},
1007 /* This function creates the class and its definition, only once */
1008 if (!uzbl.js.initialized) {
1009 /* it would be pretty cool to make this dynamic */
1010 uzbl.js.classdef = kJSClassDefinitionEmpty;
1011 uzbl.js.classdef.staticFunctions = js_static_functions;
1013 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1019 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1020 WebKitWebFrame *frame;
1021 JSGlobalContextRef context;
1022 JSObjectRef globalobject;
1023 JSStringRef var_name;
1025 JSStringRef js_script;
1026 JSValueRef js_result;
1027 JSStringRef js_result_string;
1028 size_t js_result_size;
1032 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1033 context = webkit_web_frame_get_global_context(frame);
1034 globalobject = JSContextGetGlobalObject(context);
1036 /* uzbl javascript namespace */
1037 var_name = JSStringCreateWithUTF8CString("Uzbl");
1038 JSObjectSetProperty(context, globalobject, var_name,
1039 JSObjectMake(context, uzbl.js.classref, NULL),
1040 kJSClassAttributeNone, NULL);
1042 /* evaluate the script and get return value*/
1043 js_script = JSStringCreateWithUTF8CString(script);
1044 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1045 if (js_result && !JSValueIsUndefined(context, js_result)) {
1046 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1047 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1049 if (js_result_size) {
1050 char js_result_utf8[js_result_size];
1051 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1052 g_string_assign(result, js_result_utf8);
1055 JSStringRelease(js_result_string);
1059 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1061 JSStringRelease(var_name);
1062 JSStringRelease(js_script);
1066 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1067 if (argv_idx(argv, 0))
1068 eval_js(web_view, argv_idx(argv, 0), result);
1072 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1074 if (argv_idx(argv, 0)) {
1075 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1080 while ((line = g_array_index(lines, gchar*, i))) {
1082 js = g_strdup (line);
1084 gchar* newjs = g_strconcat (js, line, NULL);
1091 if (uzbl.state.verbose)
1092 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1094 if (argv_idx (argv, 1)) {
1095 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1099 eval_js (web_view, js, result);
1101 g_array_free (lines, TRUE);
1106 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1107 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1108 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1109 webkit_web_view_unmark_text_matches (page);
1110 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1111 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1115 if (uzbl.state.searchtx) {
1116 if (uzbl.state.verbose)
1117 printf ("Searching: %s\n", uzbl.state.searchtx);
1118 webkit_web_view_set_highlight_text_matches (page, TRUE);
1119 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1124 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1126 search_text(page, argv, TRUE);
1130 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1132 search_text(page, argv, FALSE);
1136 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1137 (void) argv; (void) result;
1138 webkit_web_view_set_highlight_text_matches (page, FALSE);
1143 new_window_load_uri (const gchar * uri) {
1144 if (uzbl.behave.new_window) {
1145 GString *s = g_string_new ("");
1146 g_string_printf(s, "'%s'", uri);
1147 run_handler(uzbl.behave.new_window, s->str);
1150 GString* to_execute = g_string_new ("");
1151 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1153 for (i = 0; entries[i].long_name != NULL; i++) {
1154 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1155 gchar** str = (gchar**)entries[i].arg_data;
1157 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1161 if (uzbl.state.verbose)
1162 printf("\n%s\n", to_execute->str);
1163 g_spawn_command_line_async (to_execute->str, NULL);
1164 g_string_free (to_execute, TRUE);
1168 chain (WebKitWebView *page, GArray *argv, GString *result) {
1169 (void) page; (void) result;
1171 gchar **parts = NULL;
1173 while ((a = argv_idx(argv, i++))) {
1174 parts = g_strsplit (a, " ", 2);
1176 parse_command(parts[0], parts[1], result);
1182 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1186 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1192 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1196 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1202 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1207 int len = strlen(uzbl.state.keycmd);
1208 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1210 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1215 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1222 /* --Statusbar functions-- */
1224 build_progressbar_ascii(int percent) {
1225 int width=uzbl.gui.sbar.progress_w;
1228 GString *bar = g_string_new("");
1230 l = (double)percent*((double)width/100.);
1231 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1233 for(i=0; i<(int)l; i++)
1234 g_string_append(bar, uzbl.gui.sbar.progress_s);
1237 g_string_append(bar, uzbl.gui.sbar.progress_u);
1239 return g_string_free(bar, FALSE);
1241 /* --End Statusbar functions-- */
1244 sharg_append(GArray *a, const gchar *str) {
1245 const gchar *s = (str ? str : "");
1246 g_array_append_val(a, s);
1249 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1251 run_command (const gchar *command, const guint npre, const gchar **args,
1252 const gboolean sync, char **output_stdout) {
1253 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1256 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1257 gchar *pid = itos(getpid());
1258 gchar *xwin = itos(uzbl.xwin);
1260 sharg_append(a, command);
1261 for (i = 0; i < npre; i++) /* add n args before the default vars */
1262 sharg_append(a, args[i]);
1263 sharg_append(a, uzbl.state.config_file);
1264 sharg_append(a, pid);
1265 sharg_append(a, xwin);
1266 sharg_append(a, uzbl.comm.fifo_path);
1267 sharg_append(a, uzbl.comm.socket_path);
1268 sharg_append(a, uzbl.state.uri);
1269 sharg_append(a, uzbl.gui.main_title);
1271 for (i = npre; i < g_strv_length((gchar**)args); i++)
1272 sharg_append(a, args[i]);
1276 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1278 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1279 NULL, NULL, output_stdout, NULL, NULL, &err);
1280 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1281 NULL, NULL, NULL, &err);
1283 if (uzbl.state.verbose) {
1284 GString *s = g_string_new("spawned:");
1285 for (i = 0; i < (a->len); i++) {
1286 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1287 g_string_append_printf(s, " %s", qarg);
1290 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1291 printf("%s\n", s->str);
1292 g_string_free(s, TRUE);
1294 printf("Stdout: %s\n", *output_stdout);
1298 g_printerr("error on run_command: %s\n", err->message);
1303 g_array_free (a, TRUE);
1308 split_quoted(const gchar* src, const gboolean unquote) {
1309 /* split on unquoted space, return array of strings;
1310 remove a layer of quotes and backslashes if unquote */
1311 if (!src) return NULL;
1313 gboolean dq = FALSE;
1314 gboolean sq = FALSE;
1315 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1316 GString *s = g_string_new ("");
1320 for (p = src; *p != '\0'; p++) {
1321 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1322 else if (*p == '\\') { g_string_append_c(s, *p++);
1323 g_string_append_c(s, *p); }
1324 else if ((*p == '"') && unquote && !sq) dq = !dq;
1325 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1327 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1328 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1330 else if ((*p == ' ') && !dq && !sq) {
1331 dup = g_strdup(s->str);
1332 g_array_append_val(a, dup);
1333 g_string_truncate(s, 0);
1334 } else g_string_append_c(s, *p);
1336 dup = g_strdup(s->str);
1337 g_array_append_val(a, dup);
1338 ret = (gchar**)a->data;
1339 g_array_free (a, FALSE);
1340 g_string_free (s, TRUE);
1345 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1346 (void)web_view; (void)result;
1347 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1348 if (argv_idx(argv, 0))
1349 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1353 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1354 (void)web_view; (void)result;
1356 if (argv_idx(argv, 0))
1357 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1358 TRUE, &uzbl.comm.sync_stdout);
1362 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1363 (void)web_view; (void)result;
1364 if (!uzbl.behave.shell_cmd) {
1365 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1370 gchar *spacer = g_strdup("");
1371 g_array_insert_val(argv, 1, spacer);
1372 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1374 for (i = 1; i < g_strv_length(cmd); i++)
1375 g_array_prepend_val(argv, cmd[i]);
1377 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1383 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1384 (void)web_view; (void)result;
1385 if (!uzbl.behave.shell_cmd) {
1386 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1391 gchar *spacer = g_strdup("");
1392 g_array_insert_val(argv, 1, spacer);
1393 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1395 for (i = 1; i < g_strv_length(cmd); i++)
1396 g_array_prepend_val(argv, cmd[i]);
1398 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1399 TRUE, &uzbl.comm.sync_stdout);
1405 parse_command(const char *cmd, const char *param, GString *result) {
1408 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1410 gchar **par = split_quoted(param, TRUE);
1411 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1413 if (c->no_split) { /* don't split */
1414 sharg_append(a, param);
1416 for (i = 0; i < g_strv_length(par); i++)
1417 sharg_append(a, par[i]);
1420 if (result == NULL) {
1421 GString *result_print = g_string_new("");
1423 c->function(uzbl.gui.web_view, a, result_print);
1424 if (result_print->len)
1425 printf("%*s\n", result_print->len, result_print->str);
1427 g_string_free(result_print, TRUE);
1429 c->function(uzbl.gui.web_view, a, result);
1432 g_array_free (a, TRUE);
1435 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1442 if(*uzbl.net.proxy_url == ' '
1443 || uzbl.net.proxy_url == NULL) {
1444 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1445 (GType) SOUP_SESSION_PROXY_URI);
1448 suri = soup_uri_new(uzbl.net.proxy_url);
1449 g_object_set(G_OBJECT(uzbl.net.soup_session),
1450 SOUP_SESSION_PROXY_URI,
1452 soup_uri_free(suri);
1459 if(file_exists(uzbl.gui.icon)) {
1460 if (uzbl.gui.main_window)
1461 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1463 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1469 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1470 g_array_append_val (a, uzbl.state.uri);
1471 load_uri(uzbl.gui.web_view, a, NULL);
1472 g_array_free (a, TRUE);
1476 cmd_always_insert_mode() {
1477 set_insert_mode(uzbl.behave.always_insert_mode);
1483 g_object_set(G_OBJECT(uzbl.net.soup_session),
1484 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1488 cmd_max_conns_host() {
1489 g_object_set(G_OBJECT(uzbl.net.soup_session),
1490 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1495 soup_session_remove_feature
1496 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1497 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1498 /*g_free(uzbl.net.soup_logger);*/
1500 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1501 soup_session_add_feature(uzbl.net.soup_session,
1502 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1507 return webkit_web_view_get_settings(uzbl.gui.web_view);
1512 WebKitWebSettings *ws = view_settings();
1513 if (uzbl.behave.font_size > 0) {
1514 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1517 if (uzbl.behave.monospace_size > 0) {
1518 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1519 uzbl.behave.monospace_size, NULL);
1521 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1522 uzbl.behave.font_size, NULL);
1528 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1532 cmd_disable_plugins() {
1533 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1534 !uzbl.behave.disable_plugins, NULL);
1538 cmd_disable_scripts() {
1539 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1540 !uzbl.behave.disable_scripts, NULL);
1544 cmd_minimum_font_size() {
1545 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1546 uzbl.behave.minimum_font_size, NULL);
1549 cmd_autoload_img() {
1550 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1551 uzbl.behave.autoload_img, NULL);
1556 cmd_autoshrink_img() {
1557 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1558 uzbl.behave.autoshrink_img, NULL);
1563 cmd_enable_spellcheck() {
1564 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1565 uzbl.behave.enable_spellcheck, NULL);
1569 cmd_enable_private() {
1570 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1571 uzbl.behave.enable_private, NULL);
1576 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1577 uzbl.behave.print_bg, NULL);
1582 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1583 uzbl.behave.style_uri, NULL);
1587 cmd_resizable_txt() {
1588 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1589 uzbl.behave.resizable_txt, NULL);
1593 cmd_default_encoding() {
1594 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1595 uzbl.behave.default_encoding, NULL);
1599 cmd_enforce_96dpi() {
1600 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1601 uzbl.behave.enforce_96dpi, NULL);
1605 cmd_caret_browsing() {
1606 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1607 uzbl.behave.caret_browsing, NULL);
1611 cmd_cookie_handler() {
1612 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 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.cookie_handler);
1617 uzbl.behave.cookie_handler =
1618 g_strdup_printf("sync_%s %s", split[0], split[1]);
1625 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1626 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1627 if ((g_strcmp0(split[0], "sh") == 0) ||
1628 (g_strcmp0(split[0], "spawn") == 0)) {
1629 g_free (uzbl.behave.new_window);
1630 uzbl.behave.new_window =
1631 g_strdup_printf("%s %s", split[0], split[1]);
1638 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1643 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1648 if(uzbl.behave.inject_html) {
1649 webkit_web_view_load_html_string (uzbl.gui.web_view,
1650 uzbl.behave.inject_html, NULL);
1659 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1660 uzbl.behave.modmask = 0;
1662 if(uzbl.behave.modkey)
1663 g_free(uzbl.behave.modkey);
1664 uzbl.behave.modkey = buf;
1666 for (i = 0; modkeys[i].key != NULL; i++) {
1667 if (g_strrstr(buf, modkeys[i].key))
1668 uzbl.behave.modmask |= modkeys[i].mask;
1674 if (*uzbl.net.useragent == ' ') {
1675 g_free (uzbl.net.useragent);
1676 uzbl.net.useragent = NULL;
1678 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1679 uzbl.net.useragent, NULL);
1685 if (!uzbl.gui.scrolled_win &&
1689 gtk_widget_ref(uzbl.gui.scrolled_win);
1690 gtk_widget_ref(uzbl.gui.mainbar);
1691 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1692 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1694 if(uzbl.behave.status_top) {
1695 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1696 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1699 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1700 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1702 gtk_widget_unref(uzbl.gui.scrolled_win);
1703 gtk_widget_unref(uzbl.gui.mainbar);
1704 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1709 set_var_value(gchar *name, gchar *val) {
1710 uzbl_cmdprop *c = NULL;
1713 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1715 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1716 if(!c->writeable) return FALSE;
1718 /* check for the variable type */
1719 if (c->type == TYPE_STR) {
1720 buf = expand(val, 0);
1723 } else if(c->type == TYPE_INT) {
1724 int *ip = (int *)c->ptr;
1725 buf = expand(val, 0);
1726 *ip = (int)strtoul(buf, &endp, 10);
1728 } else if (c->type == TYPE_FLOAT) {
1729 float *fp = (float *)c->ptr;
1730 buf = expand(val, 0);
1731 *fp = strtod(buf, &endp);
1735 /* invoke a command specific function */
1736 if(c->func) c->func();
1738 /* check wether name violates our naming scheme */
1739 if(strpbrk(name, invalid_chars)) {
1740 if (uzbl.state.verbose)
1741 printf("Invalid variable name\n");
1746 c = malloc(sizeof(uzbl_cmdprop));
1751 buf = expand(val, 0);
1752 c->ptr = malloc(sizeof(char *));
1754 g_hash_table_insert(uzbl.comm.proto_var,
1755 g_strdup(name), (gpointer) c);
1762 Behaviour *b = &uzbl.behave;
1764 if(b->html_buffer->str) {
1765 webkit_web_view_load_html_string (uzbl.gui.web_view,
1766 b->html_buffer->str, b->base_url);
1767 g_string_free(b->html_buffer, TRUE);
1768 b->html_buffer = g_string_new("");
1772 enum {M_CMD, M_HTML};
1774 parse_cmd_line(const char *ctl_line, GString *result) {
1775 Behaviour *b = &uzbl.behave;
1778 if(b->mode == M_HTML) {
1779 len = strlen(b->html_endmarker);
1780 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1781 if(len == strlen(ctl_line)-1 &&
1782 !strncmp(b->html_endmarker, ctl_line, len)) {
1784 set_var_value("mode", "0");
1789 set_timeout(b->html_timeout);
1790 g_string_append(b->html_buffer, ctl_line);
1793 else if((ctl_line[0] == '#') /* Comments */
1794 || (ctl_line[0] == ' ')
1795 || (ctl_line[0] == '\n'))
1796 ; /* ignore these lines */
1797 else { /* parse a command */
1799 gchar **tokens = NULL;
1800 len = strlen(ctl_line);
1802 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1803 ctlstrip = g_strndup(ctl_line, len - 1);
1804 else ctlstrip = g_strdup(ctl_line);
1806 tokens = g_strsplit(ctlstrip, " ", 2);
1807 parse_command(tokens[0], tokens[1], result);
1814 build_stream_name(int type, const gchar* dir) {
1815 State *s = &uzbl.state;
1819 str = g_strdup_printf
1820 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1821 } else if (type == SOCKET) {
1822 str = g_strdup_printf
1823 ("%s/uzbl_socket_%s", dir, s->instance_name);
1829 control_fifo(GIOChannel *gio, GIOCondition condition) {
1830 if (uzbl.state.verbose)
1831 printf("triggered\n");
1836 if (condition & G_IO_HUP)
1837 g_error ("Fifo: Read end of pipe died!\n");
1840 g_error ("Fifo: GIOChannel broke\n");
1842 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1843 if (ret == G_IO_STATUS_ERROR) {
1844 g_error ("Fifo: Error reading: %s\n", err->message);
1848 parse_cmd_line(ctl_line, NULL);
1855 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1856 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1857 if (unlink(uzbl.comm.fifo_path) == -1)
1858 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1859 g_free(uzbl.comm.fifo_path);
1860 uzbl.comm.fifo_path = NULL;
1863 GIOChannel *chan = NULL;
1864 GError *error = NULL;
1865 gchar *path = build_stream_name(FIFO, dir);
1867 if (!file_exists(path)) {
1868 if (mkfifo (path, 0666) == 0) {
1869 // 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.
1870 chan = g_io_channel_new_file(path, "r+", &error);
1872 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1873 if (uzbl.state.verbose)
1874 printf ("init_fifo: created successfully as %s\n", path);
1875 uzbl.comm.fifo_path = path;
1877 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1878 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1879 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1880 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1882 /* if we got this far, there was an error; cleanup */
1883 if (error) g_error_free (error);
1890 control_stdin(GIOChannel *gio, GIOCondition condition) {
1892 gchar *ctl_line = NULL;
1895 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1896 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1899 parse_cmd_line(ctl_line, NULL);
1907 GIOChannel *chan = NULL;
1908 GError *error = NULL;
1910 chan = g_io_channel_unix_new(fileno(stdin));
1912 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1913 g_error ("Stdin: could not add watch\n");
1915 if (uzbl.state.verbose)
1916 printf ("Stdin: watch added successfully\n");
1919 g_error ("Stdin: Error while opening: %s\n", error->message);
1921 if (error) g_error_free (error);
1925 control_socket(GIOChannel *chan) {
1926 struct sockaddr_un remote;
1927 unsigned int t = sizeof(remote);
1929 GIOChannel *clientchan;
1931 clientsock = accept (g_io_channel_unix_get_fd(chan),
1932 (struct sockaddr *) &remote, &t);
1934 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1935 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1936 (GIOFunc) control_client_socket, clientchan);
1943 control_client_socket(GIOChannel *clientchan) {
1945 GString *result = g_string_new("");
1946 GError *error = NULL;
1950 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1951 if (ret == G_IO_STATUS_ERROR) {
1952 g_warning ("Error reading: %s\n", error->message);
1953 g_io_channel_shutdown(clientchan, TRUE, &error);
1955 } else if (ret == G_IO_STATUS_EOF) {
1956 /* shutdown and remove channel watch from main loop */
1957 g_io_channel_shutdown(clientchan, TRUE, &error);
1962 parse_cmd_line (ctl_line, result);
1963 g_string_append_c(result, '\n');
1964 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1966 if (ret == G_IO_STATUS_ERROR) {
1967 g_warning ("Error writing: %s", error->message);
1969 g_io_channel_flush(clientchan, &error);
1972 if (error) g_error_free (error);
1973 g_string_free(result, TRUE);
1979 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1980 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1981 if (unlink(uzbl.comm.socket_path) == -1)
1982 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1983 g_free(uzbl.comm.socket_path);
1984 uzbl.comm.socket_path = NULL;
1992 GIOChannel *chan = NULL;
1994 struct sockaddr_un local;
1995 gchar *path = build_stream_name(SOCKET, dir);
1997 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1999 local.sun_family = AF_UNIX;
2000 strcpy (local.sun_path, path);
2001 unlink (local.sun_path);
2003 len = strlen (local.sun_path) + sizeof (local.sun_family);
2004 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2005 if (uzbl.state.verbose)
2006 printf ("init_socket: opened in %s\n", path);
2009 if( (chan = g_io_channel_unix_new(sock)) ) {
2010 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2011 uzbl.comm.socket_path = path;
2014 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2016 /* if we got this far, there was an error; cleanup */
2023 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2024 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2026 // this function may be called very early when the templates are not set (yet), hence the checks
2028 update_title (void) {
2029 Behaviour *b = &uzbl.behave;
2032 if (b->show_status) {
2033 if (b->title_format_short) {
2034 parsed = expand(b->title_format_short, 0);
2035 if (uzbl.gui.main_window)
2036 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2039 if (b->status_format) {
2040 parsed = expand(b->status_format, 0);
2041 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2044 if (b->status_background) {
2046 gdk_color_parse (b->status_background, &color);
2047 //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)
2048 if (uzbl.gui.main_window)
2049 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2050 else if (uzbl.gui.plug)
2051 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2054 if (b->title_format_long) {
2055 parsed = expand(b->title_format_long, 0);
2056 if (uzbl.gui.main_window)
2057 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2064 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2068 retrieve_geometry();
2073 key_press_cb (GtkWidget* window, GdkEventKey* event)
2075 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2079 if (event->type != GDK_KEY_PRESS ||
2080 event->keyval == GDK_Page_Up ||
2081 event->keyval == GDK_Page_Down ||
2082 event->keyval == GDK_Up ||
2083 event->keyval == GDK_Down ||
2084 event->keyval == GDK_Left ||
2085 event->keyval == GDK_Right ||
2086 event->keyval == GDK_Shift_L ||
2087 event->keyval == GDK_Shift_R)
2090 /* turn off insert mode (if always_insert_mode is not used) */
2091 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2092 set_insert_mode(uzbl.behave.always_insert_mode);
2097 if (uzbl.behave.insert_mode &&
2098 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2099 (!uzbl.behave.modmask)
2104 if (event->keyval == GDK_Escape) {
2107 dehilight(uzbl.gui.web_view, NULL, NULL);
2111 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2112 if (event->keyval == GDK_Insert) {
2114 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2115 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2117 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2120 GString* keycmd = g_string_new(uzbl.state.keycmd);
2121 g_string_append (keycmd, str);
2122 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2129 if (event->keyval == GDK_BackSpace)
2130 keycmd_bs(NULL, NULL, NULL);
2132 gboolean key_ret = FALSE;
2133 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2136 GString* keycmd = g_string_new(uzbl.state.keycmd);
2137 g_string_append(keycmd, event->string);
2138 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2141 run_keycmd(key_ret);
2143 if (key_ret) return (!uzbl.behave.insert_mode);
2148 run_keycmd(const gboolean key_ret) {
2149 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2151 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2153 parse_command(act->name, act->param, NULL);
2157 /* try if it's an incremental keycmd or one that takes args, and run it */
2158 GString* short_keys = g_string_new ("");
2159 GString* short_keys_inc = g_string_new ("");
2161 guint len = strlen(uzbl.state.keycmd);
2162 for (i=0; i<len; i++) {
2163 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2164 g_string_assign(short_keys_inc, short_keys->str);
2165 g_string_append_c(short_keys, '_');
2166 g_string_append_c(short_keys_inc, '*');
2168 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2169 /* run normal cmds only if return was pressed */
2170 exec_paramcmd(act, i);
2173 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2174 if (key_ret) /* just quit the incremental command on return */
2176 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2180 g_string_truncate(short_keys, short_keys->len - 1);
2182 g_string_free (short_keys, TRUE);
2183 g_string_free (short_keys_inc, TRUE);
2187 exec_paramcmd(const Action *act, const guint i) {
2188 GString *parampart = g_string_new (uzbl.state.keycmd);
2189 GString *actionname = g_string_new ("");
2190 GString *actionparam = g_string_new ("");
2191 g_string_erase (parampart, 0, i+1);
2193 g_string_printf (actionname, act->name, parampart->str);
2195 g_string_printf (actionparam, act->param, parampart->str);
2196 parse_command(actionname->str, actionparam->str, NULL);
2197 g_string_free(actionname, TRUE);
2198 g_string_free(actionparam, TRUE);
2199 g_string_free(parampart, TRUE);
2207 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2209 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2210 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2211 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2212 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2213 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2214 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2215 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2216 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2217 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2218 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2219 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2226 g->mainbar = gtk_hbox_new (FALSE, 0);
2228 /* keep a reference to the bar so we can re-pack it at runtime*/
2229 //sbar_ref = g_object_ref(g->mainbar);
2231 g->mainbar_label = gtk_label_new ("");
2232 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2233 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2234 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2235 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2236 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2237 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2243 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2244 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2245 gtk_widget_set_name (window, "Uzbl browser");
2246 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2247 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2248 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2255 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2256 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2257 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2264 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2266 If actname is one that calls an external command, this function will inject
2267 newargs in front of the user-provided args in that command line. They will
2268 come become after the body of the script (in sh) or after the name of
2269 the command to execute (in spawn).
2270 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2271 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2273 The return value consist of two strings: the action (sh, ...) and its args.
2275 If act is not one that calls an external command, then the given action merely
2278 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2279 /* Arrr! Here be memory leaks */
2280 gchar *actdup = g_strdup(actname);
2281 g_array_append_val(rets, actdup);
2283 if ((g_strcmp0(actname, "spawn") == 0) ||
2284 (g_strcmp0(actname, "sh") == 0) ||
2285 (g_strcmp0(actname, "sync_spawn") == 0) ||
2286 (g_strcmp0(actname, "sync_sh") == 0)) {
2288 GString *a = g_string_new("");
2289 gchar **spawnparts = split_quoted(origargs, FALSE);
2290 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2291 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2293 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2294 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2296 g_array_append_val(rets, a->str);
2297 g_string_free(a, FALSE);
2298 g_strfreev(spawnparts);
2300 gchar *origdup = g_strdup(origargs);
2301 g_array_append_val(rets, origdup);
2303 return (gchar**)g_array_free(rets, FALSE);
2307 run_handler (const gchar *act, const gchar *args) {
2308 /* Consider this code a temporary hack to make the handlers usable.
2309 In practice, all this splicing, injection, and reconstruction is
2310 inefficient, annoying and hard to manage. Potential pitfalls arise
2311 when the handler specific args 1) are not quoted (the handler
2312 callbacks should take care of this) 2) are quoted but interfere
2313 with the users' own quotation. A more ideal solution is
2314 to refactor parse_command so that it doesn't just take a string
2315 and execute it; rather than that, we should have a function which
2316 returns the argument vector parsed from the string. This vector
2317 could be modified (e.g. insert additional args into it) before
2318 passing it to the next function that actually executes it. Though
2319 it still isn't perfect for chain actions.. will reconsider & re-
2320 factor when I have the time. -duc */
2322 char **parts = g_strsplit(act, " ", 2);
2324 if (g_strcmp0(parts[0], "chain") == 0) {
2325 GString *newargs = g_string_new("");
2326 gchar **chainparts = split_quoted(parts[1], FALSE);
2328 /* for every argument in the chain, inject the handler args
2329 and make sure the new parts are wrapped in quotes */
2330 gchar **cp = chainparts;
2332 gchar *quotless = NULL;
2333 gchar **spliced_quotless = NULL; // sigh -_-;
2334 gchar **inpart = NULL;
2337 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2339 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2340 } else quotless = g_strdup(*cp);
2342 spliced_quotless = g_strsplit(quotless, " ", 2);
2343 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2344 g_strfreev(spliced_quotless);
2346 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2352 parse_command(parts[0], &(newargs->str[1]), NULL);
2353 g_string_free(newargs, TRUE);
2354 g_strfreev(chainparts);
2357 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2358 parse_command(inparts[0], inparts[1], NULL);
2366 add_binding (const gchar *key, const gchar *act) {
2367 char **parts = g_strsplit(act, " ", 2);
2374 if (uzbl.state.verbose)
2375 printf ("Binding %-10s : %s\n", key, act);
2376 action = new_action(parts[0], parts[1]);
2378 if (g_hash_table_remove (uzbl.bindings, key))
2379 g_warning ("Overwriting existing binding for \"%s\"", key);
2380 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2385 get_xdg_var (XDG_Var xdg) {
2386 const gchar* actual_value = getenv (xdg.environmental);
2387 const gchar* home = getenv ("HOME");
2388 gchar* return_value;
2390 if (! actual_value || strcmp (actual_value, "") == 0) {
2391 if (xdg.default_value) {
2392 return_value = str_replace ("~", home, xdg.default_value);
2394 return_value = NULL;
2397 return_value = str_replace("~", home, actual_value);
2400 return return_value;
2404 find_xdg_file (int xdg_type, char* filename) {
2405 /* xdg_type = 0 => config
2406 xdg_type = 1 => data
2407 xdg_type = 2 => cache*/
2409 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2410 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2413 gchar* temporary_string;
2417 if (! file_exists (temporary_file) && xdg_type != 2) {
2418 buf = get_xdg_var (XDG[3 + xdg_type]);
2419 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2422 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2423 g_free (temporary_file);
2424 temporary_file = g_strconcat (temporary_string, filename, NULL);
2428 //g_free (temporary_string); - segfaults.
2430 if (file_exists (temporary_file)) {
2431 return temporary_file;
2438 State *s = &uzbl.state;
2439 Network *n = &uzbl.net;
2441 for (i = 0; default_config[i].command != NULL; i++) {
2442 parse_cmd_line(default_config[i].command, NULL);
2445 if (g_strcmp0(s->config_file, "-") == 0) {
2446 s->config_file = NULL;
2450 else if (!s->config_file) {
2451 s->config_file = find_xdg_file (0, "/uzbl/config");
2454 if (s->config_file) {
2455 GArray* lines = read_file_by_line (s->config_file);
2459 while ((line = g_array_index(lines, gchar*, i))) {
2460 parse_cmd_line (line, NULL);
2464 g_array_free (lines, TRUE);
2466 if (uzbl.state.verbose)
2467 printf ("No configuration file loaded.\n");
2470 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2473 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2476 if (!uzbl.behave.cookie_handler)
2479 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2480 GString *s = g_string_new ("");
2481 SoupURI * soup_uri = soup_message_get_uri(msg);
2482 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2483 run_handler(uzbl.behave.cookie_handler, s->str);
2485 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2486 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2487 if ( p != NULL ) *p = '\0';
2488 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2490 if (uzbl.comm.sync_stdout)
2491 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2493 g_string_free(s, TRUE);
2497 save_cookies (SoupMessage *msg, gpointer user_data){
2501 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2502 cookie = soup_cookie_to_set_cookie_header(ck->data);
2503 SoupURI * soup_uri = soup_message_get_uri(msg);
2504 GString *s = g_string_new ("");
2505 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2506 run_handler(uzbl.behave.cookie_handler, s->str);
2508 g_string_free(s, TRUE);
2513 /* --- WEBINSPECTOR --- */
2515 hide_window_cb(GtkWidget *widget, gpointer data) {
2518 gtk_widget_hide(widget);
2522 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2525 (void) web_inspector;
2526 GtkWidget* scrolled_window;
2527 GtkWidget* new_web_view;
2530 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2531 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2532 G_CALLBACK(hide_window_cb), NULL);
2534 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2535 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2536 gtk_widget_show(g->inspector_window);
2538 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2539 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2540 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2541 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2542 gtk_widget_show(scrolled_window);
2544 new_web_view = webkit_web_view_new();
2545 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2547 return WEBKIT_WEB_VIEW(new_web_view);
2551 inspector_show_window_cb (WebKitWebInspector* inspector){
2553 gtk_widget_show(uzbl.gui.inspector_window);
2557 /* TODO: Add variables and code to make use of these functions */
2559 inspector_close_window_cb (WebKitWebInspector* inspector){
2565 inspector_attach_window_cb (WebKitWebInspector* inspector){
2571 inspector_detach_window_cb (WebKitWebInspector* inspector){
2577 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2583 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2589 set_up_inspector() {
2591 WebKitWebSettings *settings = view_settings();
2592 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2594 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2595 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2596 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2597 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2598 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2599 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2600 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2602 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2606 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2608 uzbl_cmdprop *c = v;
2613 if(c->type == TYPE_STR)
2614 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2615 else if(c->type == TYPE_INT)
2616 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2617 else if(c->type == TYPE_FLOAT)
2618 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2622 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2626 printf("bind %s = %s %s\n", (char *)k ,
2627 (char *)a->name, a->param?(char *)a->param:"");
2632 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2633 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2637 retrieve_geometry() {
2639 GString *buf = g_string_new("");
2641 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2642 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2644 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2646 if(uzbl.gui.geometry)
2647 g_free(uzbl.gui.geometry);
2648 uzbl.gui.geometry = g_string_free(buf, FALSE);
2651 /* set up gtk, gobject, variable defaults and other things that tests and other
2652 * external applications need to do anyhow */
2654 initialize(int argc, char *argv[]) {
2655 if (!g_thread_supported ())
2656 g_thread_init (NULL);
2657 uzbl.state.executable_path = g_strdup(argv[0]);
2658 uzbl.state.selected_url = NULL;
2659 uzbl.state.searchtx = NULL;
2661 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2662 g_option_context_add_main_entries (context, entries, NULL);
2663 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2664 g_option_context_parse (context, &argc, &argv, NULL);
2665 g_option_context_free(context);
2667 if (uzbl.behave.print_version) {
2668 printf("Commit: %s\n", COMMIT);
2672 /* initialize hash table */
2673 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2675 uzbl.net.soup_session = webkit_get_default_session();
2676 uzbl.state.keycmd = g_strdup("");
2678 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2679 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2680 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2681 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2682 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2683 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2685 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2686 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2687 uzbl.gui.sbar.progress_w = 10;
2689 /* HTML mode defaults*/
2690 uzbl.behave.html_buffer = g_string_new("");
2691 uzbl.behave.html_endmarker = g_strdup(".");
2692 uzbl.behave.html_timeout = 60;
2693 uzbl.behave.base_url = g_strdup("http://invalid");
2695 /* default mode indicators */
2696 uzbl.behave.insert_indicator = g_strdup("I");
2697 uzbl.behave.cmd_indicator = g_strdup("C");
2699 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2700 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2701 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2702 uzbl.info.arch = ARCH;
2703 uzbl.info.commit = COMMIT;
2706 make_var_to_name_hash();
2711 #ifndef UZBL_LIBRARY
2714 main (int argc, char* argv[]) {
2715 initialize(argc, argv);
2717 gtk_init (&argc, &argv);
2719 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2720 //main_window_ref = g_object_ref(scrolled_window);
2721 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2722 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2724 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2725 GTK_WIDGET (uzbl.gui.web_view));
2727 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2731 /* initial packing */
2732 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2733 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2735 if (uzbl.state.socket_id) {
2736 uzbl.gui.plug = create_plug ();
2737 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2738 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2740 uzbl.gui.main_window = create_window ();
2741 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2742 gtk_widget_show_all (uzbl.gui.main_window);
2743 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2746 if(!uzbl.state.instance_name)
2747 uzbl.state.instance_name = itos((int)uzbl.xwin);
2749 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2751 if (uzbl.state.verbose) {
2752 printf("Uzbl start location: %s\n", argv[0]);
2753 if (uzbl.state.socket_id)
2754 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2756 printf("window_id %i\n",(int) uzbl.xwin);
2757 printf("pid %i\n", getpid ());
2758 printf("name: %s\n", uzbl.state.instance_name);
2761 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2762 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2763 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2764 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2765 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2767 if(uzbl.gui.geometry)
2770 retrieve_geometry();
2772 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2773 if (argc > 1 && !uzbl.state.uri)
2774 uri_override = g_strdup(argv[1]);
2775 gboolean verbose_override = uzbl.state.verbose;
2778 set_insert_mode(FALSE);
2780 if (!uzbl.behave.show_status)
2781 gtk_widget_hide(uzbl.gui.mainbar);
2788 if (verbose_override > uzbl.state.verbose)
2789 uzbl.state.verbose = verbose_override;
2792 set_var_value("uri", uri_override);
2793 g_free(uri_override);
2794 } else if (uzbl.state.uri)
2800 return EXIT_SUCCESS;
2804 /* vi: set et ts=4: */