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>
58 #include <sys/ioctl.h>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Path to config file or '-' for stdin", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
79 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
81 "Print the version and exit", NULL },
82 { NULL, 0, 0, 0, NULL, NULL, NULL }
85 /* associate command names to their properties */
86 typedef const struct {
87 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
88 the PTR() macro is kind of preventing this change at the moment. */
96 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
98 /* abbreviations to help keep the table's width humane */
99 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
100 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
105 } var_name_to_ptr[] = {
106 /* variable name pointer to variable in code type dump callback function */
107 /* ---------------------------------------------------------------------------------------------- */
108 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
109 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
110 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
111 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
112 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
113 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
114 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
115 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
116 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
117 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
118 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
119 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
120 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
121 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
122 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
123 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
124 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
125 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
126 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
127 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
128 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
129 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
130 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
131 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
132 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
133 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
134 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
135 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
136 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
137 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
138 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
139 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
140 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
141 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
142 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
143 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
144 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
145 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
146 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
147 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
149 /* exported WebKitWebSettings properties */
150 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
151 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
152 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
153 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
154 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
155 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
156 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
157 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
158 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
159 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
160 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
161 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
162 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
163 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
164 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
165 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
167 /* constants (not dumpable or writeable) */
168 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
169 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
170 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
171 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
172 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
173 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
174 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
175 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
176 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
177 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
178 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
180 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
181 }, *n2v_p = var_name_to_ptr;
188 { "SHIFT", GDK_SHIFT_MASK }, // shift
189 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
190 { "CONTROL", GDK_CONTROL_MASK }, // control
191 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
192 { "MOD2", GDK_MOD2_MASK }, // 5th mod
193 { "MOD3", GDK_MOD3_MASK }, // 6th mod
194 { "MOD4", GDK_MOD4_MASK }, // 7th mod
195 { "MOD5", GDK_MOD5_MASK }, // 8th mod
196 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
197 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
198 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
199 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
200 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
201 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
202 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
203 { "META", GDK_META_MASK }, // meta (since 2.10)
208 /* construct a hash from the var_name_to_ptr array for quick access */
210 make_var_to_name_hash() {
211 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
213 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
218 /* --- UTILITY FUNCTIONS --- */
219 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
221 get_exp_type(gchar *s) {
225 else if(*(s+1) == '{')
226 return EXP_BRACED_VAR;
227 else if(*(s+1) == '<')
229 else if(*(s+1) == '[')
232 return EXP_SIMPLE_VAR;
238 * recurse == 1: don't expand '@(command)@'
239 * recurse == 2: don't expand '@<java script>@'
242 expand(char *s, guint recurse) {
246 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
251 gchar *cmd_stdout = NULL;
253 GString *buf = g_string_new("");
254 GString *js_ret = g_string_new("");
259 g_string_append_c(buf, *++s);
264 etype = get_exp_type(s);
269 vend = strpbrk(s, end_simple_var);
270 if(!vend) vend = strchr(s, '\0');
274 vend = strchr(s, upto);
275 if(!vend) vend = strchr(s, '\0');
279 strcpy(str_end, ")@");
281 vend = strstr(s, str_end);
282 if(!vend) vend = strchr(s, '\0');
286 strcpy(str_end, ">@");
288 vend = strstr(s, str_end);
289 if(!vend) vend = strchr(s, '\0');
293 strcpy(str_end, "]@");
295 vend = strstr(s, str_end);
296 if(!vend) vend = strchr(s, '\0');
301 strncpy(ret, s, vend-s);
305 if(etype == EXP_SIMPLE_VAR ||
306 etype == EXP_BRACED_VAR) {
307 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
308 if(c->type == TYPE_STR && *c->ptr != NULL) {
309 g_string_append(buf, (gchar *)*c->ptr);
310 } else if(c->type == TYPE_INT) {
311 g_string_append_printf(buf, "%d", (int)*c->ptr);
313 else if(c->type == TYPE_FLOAT) {
314 g_string_append_printf(buf, "%f", *(float *)c->ptr);
318 if(etype == EXP_SIMPLE_VAR)
323 else if(recurse != 1 &&
325 mycmd = expand(ret, 1);
326 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
330 g_printerr("error on running command: %s\n", err->message);
333 else if (*cmd_stdout) {
334 int len = strlen(cmd_stdout);
336 if(cmd_stdout[len-1] == '\n')
337 cmd_stdout[--len] = 0; /* strip trailing newline */
339 g_string_append(buf, cmd_stdout);
344 else if(recurse != 2 &&
346 mycmd = expand(ret, 2);
347 eval_js(uzbl.gui.web_view, mycmd, js_ret);
351 g_string_append(buf, js_ret->str);
352 g_string_free(js_ret, TRUE);
353 js_ret = g_string_new("");
357 else if(etype == EXP_ESCAPE) {
358 mycmd = expand(ret, 0);
359 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
361 g_string_append(buf, escaped);
370 g_string_append_c(buf, *s);
375 g_string_free(js_ret, TRUE);
376 return g_string_free(buf, FALSE);
383 snprintf(tmp, sizeof(tmp), "%i", val);
384 return g_strdup(tmp);
388 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
391 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
394 str_replace (const char* search, const char* replace, const char* string) {
398 buf = g_strsplit (string, search, -1);
399 ret = g_strjoinv (replace, buf);
400 g_strfreev(buf); // somebody said this segfaults
406 read_file_by_line (gchar *path) {
407 GIOChannel *chan = NULL;
408 gchar *readbuf = NULL;
410 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
413 chan = g_io_channel_new_file(path, "r", NULL);
416 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
417 const gchar* val = g_strdup (readbuf);
418 g_array_append_val (lines, val);
423 g_io_channel_unref (chan);
425 fprintf(stderr, "File '%s' not be read.\n", path);
432 parseenv (char* string) {
433 extern char** environ;
434 gchar* tmpstr = NULL;
438 while (environ[i] != NULL) {
439 gchar** env = g_strsplit (environ[i], "=", 2);
440 gchar* envname = g_strconcat ("$", env[0], NULL);
442 if (g_strrstr (string, envname) != NULL) {
443 tmpstr = g_strdup(string);
445 string = str_replace(envname, env[1], tmpstr);
450 g_strfreev (env); // somebody said this breaks uzbl
458 setup_signal(int signr, sigfunc *shandler) {
459 struct sigaction nh, oh;
461 nh.sa_handler = shandler;
462 sigemptyset(&nh.sa_mask);
465 if(sigaction(signr, &nh, &oh) < 0)
473 if (uzbl.behave.fifo_dir)
474 unlink (uzbl.comm.fifo_path);
475 if (uzbl.behave.socket_dir)
476 unlink (uzbl.comm.socket_path);
478 g_free(uzbl.state.executable_path);
479 g_free(uzbl.state.keycmd);
480 g_hash_table_destroy(uzbl.bindings);
481 g_hash_table_destroy(uzbl.behave.commands);
484 /* used for html_mode_timeout
485 * be sure to extend this function to use
486 * more timers if needed in other places
489 set_timeout(int seconds) {
491 memset(&t, 0, sizeof t);
493 t.it_value.tv_sec = seconds;
494 t.it_value.tv_usec = 0;
495 setitimer(ITIMER_REAL, &t, NULL);
498 /* --- SIGNAL HANDLER --- */
501 catch_sigterm(int s) {
507 catch_sigint(int s) {
517 set_var_value("mode", "0");
522 /* --- CALLBACKS --- */
525 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
528 (void) navigation_action;
529 (void) policy_decision;
531 const gchar* uri = webkit_network_request_get_uri (request);
532 if (uzbl.state.verbose)
533 printf("New window requested -> %s \n", uri);
534 webkit_web_policy_decision_use(policy_decision);
539 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
544 /* If we can display it, let's display it... */
545 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
546 webkit_web_policy_decision_use (policy_decision);
550 /* ...everything we can't displayed is downloaded */
551 webkit_web_policy_decision_download (policy_decision);
556 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
560 if (uzbl.state.selected_url != NULL) {
561 if (uzbl.state.verbose)
562 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
563 new_window_load_uri(uzbl.state.selected_url);
565 if (uzbl.state.verbose)
566 printf("New web view -> %s\n","Nothing to open, exiting");
572 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
575 if (uzbl.behave.download_handler) {
576 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
577 if (uzbl.state.verbose)
578 printf("Download -> %s\n",uri);
579 /* if urls not escaped, we may have to escape and quote uri before this call */
580 run_handler(uzbl.behave.download_handler, uri);
585 /* scroll a bar in a given direction */
587 scroll (GtkAdjustment* bar, GArray *argv) {
591 gdouble page_size = gtk_adjustment_get_page_size(bar);
592 gdouble value = gtk_adjustment_get_value(bar);
593 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
596 value += page_size * amount * 0.01;
600 max_value = gtk_adjustment_get_upper(bar) - page_size;
602 if (value > max_value)
603 value = max_value; /* don't scroll past the end of the page */
605 gtk_adjustment_set_value (bar, value);
609 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
610 (void) page; (void) argv; (void) result;
611 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
615 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
616 (void) page; (void) argv; (void) result;
617 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
618 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
622 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
623 (void) page; (void) result;
624 scroll(uzbl.gui.bar_v, argv);
628 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
629 (void) page; (void) result;
630 scroll(uzbl.gui.bar_h, argv);
635 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
636 if(uzbl.state.verbose)
637 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
639 /* update geometry var with the actual geometry
640 this is necessary as some WMs don't seem to honour
641 the above setting and we don't want to end up with
642 wrong geometry information
649 if (!uzbl.behave.show_status) {
650 gtk_widget_hide(uzbl.gui.mainbar);
652 gtk_widget_show(uzbl.gui.mainbar);
658 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
663 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
667 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
672 if (uzbl.behave.show_status) {
673 gtk_widget_hide(uzbl.gui.mainbar);
675 gtk_widget_show(uzbl.gui.mainbar);
677 uzbl.behave.show_status = !uzbl.behave.show_status;
682 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
686 //Set selected_url state variable
687 g_free(uzbl.state.selected_url);
688 uzbl.state.selected_url = NULL;
690 uzbl.state.selected_url = g_strdup(link);
696 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
699 const gchar *title = webkit_web_view_get_title(web_view);
700 if (uzbl.gui.main_title)
701 g_free (uzbl.gui.main_title);
702 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
707 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
710 uzbl.gui.sbar.load_progress = progress;
712 g_free(uzbl.gui.sbar.progress_bar);
713 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
719 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
723 if (uzbl.behave.load_finish_handler)
724 run_handler(uzbl.behave.load_finish_handler, "");
727 void clear_keycmd() {
728 g_free(uzbl.state.keycmd);
729 uzbl.state.keycmd = g_strdup("");
733 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
737 uzbl.gui.sbar.load_progress = 0;
738 clear_keycmd(); // don't need old commands to remain on new page?
739 if (uzbl.behave.load_start_handler)
740 run_handler(uzbl.behave.load_start_handler, "");
744 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
747 g_free (uzbl.state.uri);
748 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
749 uzbl.state.uri = g_string_free (newuri, FALSE);
750 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
751 set_insert_mode(uzbl.behave.always_insert_mode);
754 if (uzbl.behave.load_commit_handler)
755 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
759 destroy_cb (GtkWidget* widget, gpointer data) {
767 if (uzbl.behave.history_handler) {
769 struct tm * timeinfo;
772 timeinfo = localtime ( &rawtime );
773 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
774 run_handler(uzbl.behave.history_handler, date);
779 /* VIEW funcs (little webkit wrappers) */
780 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
782 VIEWFUNC(reload_bypass_cache)
783 VIEWFUNC(stop_loading)
790 /* -- command to callback/function map for things we cannot attach to any signals */
791 struct {char *key; CommandInfo value;} cmdlist[] =
792 { /* key function no_split */
793 { "back", {view_go_back, 0} },
794 { "forward", {view_go_forward, 0} },
795 { "scroll_vert", {scroll_vert, 0} },
796 { "scroll_horz", {scroll_horz, 0} },
797 { "scroll_begin", {scroll_begin, 0} },
798 { "scroll_end", {scroll_end, 0} },
799 { "reload", {view_reload, 0}, },
800 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
801 { "stop", {view_stop_loading, 0}, },
802 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
803 { "zoom_out", {view_zoom_out, 0}, },
804 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
805 { "uri", {load_uri, TRUE} },
806 { "js", {run_js, TRUE} },
807 { "script", {run_external_js, 0} },
808 { "toggle_status", {toggle_status_cb, 0} },
809 { "spawn", {spawn, 0} },
810 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
811 { "sh", {spawn_sh, 0} },
812 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
813 { "talk_to_socket", {talk_to_socket, TRUE} },
814 { "exit", {close_uzbl, 0} },
815 { "search", {search_forward_text, TRUE} },
816 { "search_reverse", {search_reverse_text, TRUE} },
817 { "dehilight", {dehilight, 0} },
818 { "toggle_insert_mode", {toggle_insert_mode, 0} },
819 { "set", {set_var, TRUE} },
820 //{ "get", {get_var, TRUE} },
821 { "bind", {act_bind, TRUE} },
822 { "dump_config", {act_dump_config, 0} },
823 { "keycmd", {keycmd, TRUE} },
824 { "keycmd_nl", {keycmd_nl, TRUE} },
825 { "keycmd_bs", {keycmd_bs, 0} },
826 { "chain", {chain, 0} },
827 { "print", {print, TRUE} }
834 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
836 for (i = 0; i < LENGTH(cmdlist); i++)
837 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
840 /* -- CORE FUNCTIONS -- */
843 free_action(gpointer act) {
844 Action *action = (Action*)act;
845 g_free(action->name);
847 g_free(action->param);
852 new_action(const gchar *name, const gchar *param) {
853 Action *action = g_new(Action, 1);
855 action->name = g_strdup(name);
857 action->param = g_strdup(param);
859 action->param = NULL;
865 file_exists (const char * filename) {
866 return (access(filename, F_OK) == 0);
870 set_var(WebKitWebView *page, GArray *argv, GString *result) {
871 (void) page; (void) result;
872 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
873 if (split[0] != NULL) {
874 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
875 set_var_value(g_strstrip(split[0]), value);
882 print(WebKitWebView *page, GArray *argv, GString *result) {
883 (void) page; (void) result;
886 buf = expand(argv_idx(argv, 0), 0);
887 g_string_assign(result, buf);
892 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
893 (void) page; (void) result;
894 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
895 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
896 add_binding(g_strstrip(split[0]), value);
914 set_mode_indicator() {
915 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
916 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
921 set_mode_indicator();
926 set_insert_mode(gboolean mode) {
927 uzbl.behave.insert_mode = mode;
928 set_mode_indicator();
932 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
933 (void) page; (void) result;
935 if (argv_idx(argv, 0)) {
936 if (strcmp (argv_idx(argv, 0), "0") == 0) {
937 set_insert_mode(FALSE);
939 set_insert_mode(TRUE);
942 set_insert_mode( !uzbl.behave.insert_mode );
949 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
952 if (argv_idx(argv, 0)) {
953 GString* newuri = g_string_new (argv_idx(argv, 0));
954 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
955 run_js(web_view, argv, NULL);
958 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
959 g_string_prepend (newuri, "http://");
960 /* if we do handle cookies, ask our handler for them */
961 webkit_web_view_load_uri (web_view, newuri->str);
962 g_string_free (newuri, TRUE);
969 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
970 size_t argumentCount, const JSValueRef arguments[],
971 JSValueRef* exception) {
976 JSStringRef js_result_string;
977 GString *result = g_string_new("");
979 if (argumentCount >= 1) {
980 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
981 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
982 char ctl_line[arg_size];
983 JSStringGetUTF8CString(arg, ctl_line, arg_size);
985 parse_cmd_line(ctl_line, result);
987 JSStringRelease(arg);
989 js_result_string = JSStringCreateWithUTF8CString(result->str);
991 g_string_free(result, TRUE);
993 return JSValueMakeString(ctx, js_result_string);
996 JSStaticFunction js_static_functions[] = {
997 {"run", js_run_command, kJSPropertyAttributeNone},
1002 /* This function creates the class and its definition, only once */
1003 if (!uzbl.js.initialized) {
1004 /* it would be pretty cool to make this dynamic */
1005 uzbl.js.classdef = kJSClassDefinitionEmpty;
1006 uzbl.js.classdef.staticFunctions = js_static_functions;
1008 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1014 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1015 WebKitWebFrame *frame;
1016 JSGlobalContextRef context;
1017 JSObjectRef globalobject;
1018 JSStringRef var_name;
1020 JSStringRef js_script;
1021 JSValueRef js_result;
1022 JSStringRef js_result_string;
1023 size_t js_result_size;
1027 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1028 context = webkit_web_frame_get_global_context(frame);
1029 globalobject = JSContextGetGlobalObject(context);
1031 /* uzbl javascript namespace */
1032 var_name = JSStringCreateWithUTF8CString("Uzbl");
1033 JSObjectSetProperty(context, globalobject, var_name,
1034 JSObjectMake(context, uzbl.js.classref, NULL),
1035 kJSClassAttributeNone, NULL);
1037 /* evaluate the script and get return value*/
1038 js_script = JSStringCreateWithUTF8CString(script);
1039 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1040 if (js_result && !JSValueIsUndefined(context, js_result)) {
1041 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1042 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1044 if (js_result_size) {
1045 char js_result_utf8[js_result_size];
1046 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1047 g_string_assign(result, js_result_utf8);
1050 JSStringRelease(js_result_string);
1054 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1056 JSStringRelease(var_name);
1057 JSStringRelease(js_script);
1061 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1062 if (argv_idx(argv, 0))
1063 eval_js(web_view, argv_idx(argv, 0), result);
1067 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1069 if (argv_idx(argv, 0)) {
1070 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1075 while ((line = g_array_index(lines, gchar*, i))) {
1077 js = g_strdup (line);
1079 gchar* newjs = g_strconcat (js, line, NULL);
1086 if (uzbl.state.verbose)
1087 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1089 if (argv_idx (argv, 1)) {
1090 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1094 eval_js (web_view, js, result);
1096 g_array_free (lines, TRUE);
1101 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1102 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1103 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1104 webkit_web_view_unmark_text_matches (page);
1105 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1106 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1110 if (uzbl.state.searchtx) {
1111 if (uzbl.state.verbose)
1112 printf ("Searching: %s\n", uzbl.state.searchtx);
1113 webkit_web_view_set_highlight_text_matches (page, TRUE);
1114 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1119 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1121 search_text(page, argv, TRUE);
1125 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1127 search_text(page, argv, FALSE);
1131 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1132 (void) argv; (void) result;
1133 webkit_web_view_set_highlight_text_matches (page, FALSE);
1138 new_window_load_uri (const gchar * uri) {
1139 if (uzbl.behave.new_window) {
1140 GString *s = g_string_new ("");
1141 g_string_printf(s, "'%s'", uri);
1142 run_handler(uzbl.behave.new_window, s->str);
1145 GString* to_execute = g_string_new ("");
1146 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1148 for (i = 0; entries[i].long_name != NULL; i++) {
1149 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1150 gchar** str = (gchar**)entries[i].arg_data;
1152 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1156 if (uzbl.state.verbose)
1157 printf("\n%s\n", to_execute->str);
1158 g_spawn_command_line_async (to_execute->str, NULL);
1159 g_string_free (to_execute, TRUE);
1163 chain (WebKitWebView *page, GArray *argv, GString *result) {
1164 (void) page; (void) result;
1166 gchar **parts = NULL;
1168 while ((a = argv_idx(argv, i++))) {
1169 parts = g_strsplit (a, " ", 2);
1171 parse_command(parts[0], parts[1], result);
1177 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1181 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1187 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1191 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1197 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1202 int len = strlen(uzbl.state.keycmd);
1203 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1205 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1210 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1217 /* --Statusbar functions-- */
1219 build_progressbar_ascii(int percent) {
1220 int width=uzbl.gui.sbar.progress_w;
1223 GString *bar = g_string_new("");
1225 l = (double)percent*((double)width/100.);
1226 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1228 for(i=0; i<(int)l; i++)
1229 g_string_append(bar, uzbl.gui.sbar.progress_s);
1232 g_string_append(bar, uzbl.gui.sbar.progress_u);
1234 return g_string_free(bar, FALSE);
1236 /* --End Statusbar functions-- */
1239 sharg_append(GArray *a, const gchar *str) {
1240 const gchar *s = (str ? str : "");
1241 g_array_append_val(a, s);
1244 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1246 run_command (const gchar *command, const guint npre, const gchar **args,
1247 const gboolean sync, char **output_stdout) {
1248 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1251 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1252 gchar *pid = itos(getpid());
1253 gchar *xwin = itos(uzbl.xwin);
1255 sharg_append(a, command);
1256 for (i = 0; i < npre; i++) /* add n args before the default vars */
1257 sharg_append(a, args[i]);
1258 sharg_append(a, uzbl.state.config_file);
1259 sharg_append(a, pid);
1260 sharg_append(a, xwin);
1261 sharg_append(a, uzbl.comm.fifo_path);
1262 sharg_append(a, uzbl.comm.socket_path);
1263 sharg_append(a, uzbl.state.uri);
1264 sharg_append(a, uzbl.gui.main_title);
1266 for (i = npre; i < g_strv_length((gchar**)args); i++)
1267 sharg_append(a, args[i]);
1271 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1273 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1274 NULL, NULL, output_stdout, NULL, NULL, &err);
1275 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1276 NULL, NULL, NULL, &err);
1278 if (uzbl.state.verbose) {
1279 GString *s = g_string_new("spawned:");
1280 for (i = 0; i < (a->len); i++) {
1281 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1282 g_string_append_printf(s, " %s", qarg);
1285 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1286 printf("%s\n", s->str);
1287 g_string_free(s, TRUE);
1289 printf("Stdout: %s\n", *output_stdout);
1293 g_printerr("error on run_command: %s\n", err->message);
1298 g_array_free (a, TRUE);
1303 split_quoted(const gchar* src, const gboolean unquote) {
1304 /* split on unquoted space, return array of strings;
1305 remove a layer of quotes and backslashes if unquote */
1306 if (!src) return NULL;
1308 gboolean dq = FALSE;
1309 gboolean sq = FALSE;
1310 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311 GString *s = g_string_new ("");
1315 for (p = src; *p != '\0'; p++) {
1316 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1317 else if (*p == '\\') { g_string_append_c(s, *p++);
1318 g_string_append_c(s, *p); }
1319 else if ((*p == '"') && unquote && !sq) dq = !dq;
1320 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1322 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1323 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1325 else if ((*p == ' ') && !dq && !sq) {
1326 dup = g_strdup(s->str);
1327 g_array_append_val(a, dup);
1328 g_string_truncate(s, 0);
1329 } else g_string_append_c(s, *p);
1331 dup = g_strdup(s->str);
1332 g_array_append_val(a, dup);
1333 ret = (gchar**)a->data;
1334 g_array_free (a, FALSE);
1335 g_string_free (s, TRUE);
1340 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1341 (void)web_view; (void)result;
1342 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1343 if (argv_idx(argv, 0))
1344 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1348 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1349 (void)web_view; (void)result;
1351 if (argv_idx(argv, 0))
1352 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1353 TRUE, &uzbl.comm.sync_stdout);
1357 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1358 (void)web_view; (void)result;
1359 if (!uzbl.behave.shell_cmd) {
1360 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1365 gchar *spacer = g_strdup("");
1366 g_array_insert_val(argv, 1, spacer);
1367 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1369 for (i = 1; i < g_strv_length(cmd); i++)
1370 g_array_prepend_val(argv, cmd[i]);
1372 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1378 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1379 (void)web_view; (void)result;
1380 if (!uzbl.behave.shell_cmd) {
1381 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1386 gchar *spacer = g_strdup("");
1387 g_array_insert_val(argv, 1, spacer);
1388 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1390 for (i = 1; i < g_strv_length(cmd); i++)
1391 g_array_prepend_val(argv, cmd[i]);
1393 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1394 TRUE, &uzbl.comm.sync_stdout);
1400 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1401 (void)web_view; (void)result;
1404 struct sockaddr_un sa;
1405 char* sockpath, * cmd;
1406 ssize_t cmd_len, ret;
1409 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1411 /* This function could be optimised by storing a hash table of socket paths
1412 and associated connected file descriptors rather than closing and
1413 re-opening for every call. Also we could launch a script if socket connect
1416 /* Test arguments passed in correctly: argv should be an array of one element,
1417 which should be a string consisting of the socket path followed by a space
1418 followed by the command to write to the socket. Check that the socket path
1419 is valid (starts with '/' and fits in a sockaddr_un.sun_path[]). */
1420 if(argv->len != 1) {
1421 g_printerr("talk_to_socket called with argv->len %d\n", (int)(argv->len));
1425 sockpath = g_array_index(argv, char*, 0);
1426 cmd = strchr(sockpath, ' ');
1427 if(!cmd || *sockpath != '/' || (cmd - sockpath) >= (int)sizeof(sa.sun_path)) {
1428 g_printerr("talk_to_socket called incorrectly (%s)\n", sockpath);
1432 /* copy socket path, null terminate result */
1433 memcpy(sa.sun_path, sockpath, (cmd - sockpath));
1434 sa.sun_path[cmd - sockpath] = 0;
1435 sa.sun_family = AF_UNIX;
1437 /* point to start of command, find its length in bytes */
1439 cmd_len = strlen(cmd);
1441 /* create socket file descriptor and connect it to path */
1442 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1444 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1447 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1448 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1454 ret = write(fd, cmd, cmd_len);
1456 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1461 /* wait for a response, with a 500ms timeout */
1463 pfd.events = POLLIN;
1465 ret = poll(&pfd, 1, 500);
1467 if(ret == 0) errno = ETIMEDOUT;
1468 if(errno == EINTR) continue;
1469 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1475 /* get length of response */
1476 if(ioctl(fd, FIONREAD, &len) == -1) {
1477 g_printerr("talk_to_socket: cannot find daemon response length, "
1478 "ioctl failed (%s)\n", strerror(errno));
1483 /* if there is a response, read it */
1485 uzbl.comm.sync_stdout = g_malloc(len + 1);
1486 if(!uzbl.comm.sync_stdout) {
1487 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1491 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1493 ret = read(fd, uzbl.comm.sync_stdout, len);
1495 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1508 parse_command(const char *cmd, const char *param, GString *result) {
1511 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1513 gchar **par = split_quoted(param, TRUE);
1514 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1516 if (c->no_split) { /* don't split */
1517 sharg_append(a, param);
1519 for (i = 0; i < g_strv_length(par); i++)
1520 sharg_append(a, par[i]);
1523 if (result == NULL) {
1524 GString *result_print = g_string_new("");
1526 c->function(uzbl.gui.web_view, a, result_print);
1527 if (result_print->len)
1528 printf("%*s\n", result_print->len, result_print->str);
1530 g_string_free(result_print, TRUE);
1532 c->function(uzbl.gui.web_view, a, result);
1535 g_array_free (a, TRUE);
1538 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1545 if(*uzbl.net.proxy_url == ' '
1546 || uzbl.net.proxy_url == NULL) {
1547 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1548 (GType) SOUP_SESSION_PROXY_URI);
1551 suri = soup_uri_new(uzbl.net.proxy_url);
1552 g_object_set(G_OBJECT(uzbl.net.soup_session),
1553 SOUP_SESSION_PROXY_URI,
1555 soup_uri_free(suri);
1562 if(file_exists(uzbl.gui.icon)) {
1563 if (uzbl.gui.main_window)
1564 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1566 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1572 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1573 g_array_append_val (a, uzbl.state.uri);
1574 load_uri(uzbl.gui.web_view, a, NULL);
1575 g_array_free (a, TRUE);
1579 cmd_always_insert_mode() {
1580 set_insert_mode(uzbl.behave.always_insert_mode);
1586 g_object_set(G_OBJECT(uzbl.net.soup_session),
1587 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1591 cmd_max_conns_host() {
1592 g_object_set(G_OBJECT(uzbl.net.soup_session),
1593 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1598 soup_session_remove_feature
1599 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1600 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1601 /*g_free(uzbl.net.soup_logger);*/
1603 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1604 soup_session_add_feature(uzbl.net.soup_session,
1605 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1610 return webkit_web_view_get_settings(uzbl.gui.web_view);
1615 WebKitWebSettings *ws = view_settings();
1616 if (uzbl.behave.font_size > 0) {
1617 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1620 if (uzbl.behave.monospace_size > 0) {
1621 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1622 uzbl.behave.monospace_size, NULL);
1624 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1625 uzbl.behave.font_size, NULL);
1631 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1635 cmd_disable_plugins() {
1636 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1637 !uzbl.behave.disable_plugins, NULL);
1641 cmd_disable_scripts() {
1642 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1643 !uzbl.behave.disable_scripts, NULL);
1647 cmd_minimum_font_size() {
1648 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1649 uzbl.behave.minimum_font_size, NULL);
1652 cmd_autoload_img() {
1653 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1654 uzbl.behave.autoload_img, NULL);
1659 cmd_autoshrink_img() {
1660 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1661 uzbl.behave.autoshrink_img, NULL);
1666 cmd_enable_spellcheck() {
1667 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1668 uzbl.behave.enable_spellcheck, NULL);
1672 cmd_enable_private() {
1673 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1674 uzbl.behave.enable_private, NULL);
1679 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1680 uzbl.behave.print_bg, NULL);
1685 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1686 uzbl.behave.style_uri, NULL);
1690 cmd_resizable_txt() {
1691 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1692 uzbl.behave.resizable_txt, NULL);
1696 cmd_default_encoding() {
1697 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1698 uzbl.behave.default_encoding, NULL);
1702 cmd_enforce_96dpi() {
1703 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1704 uzbl.behave.enforce_96dpi, NULL);
1708 cmd_caret_browsing() {
1709 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1710 uzbl.behave.caret_browsing, NULL);
1714 cmd_cookie_handler() {
1715 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1716 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1717 if ((g_strcmp0(split[0], "sh") == 0) ||
1718 (g_strcmp0(split[0], "spawn") == 0)) {
1719 g_free (uzbl.behave.cookie_handler);
1720 uzbl.behave.cookie_handler =
1721 g_strdup_printf("sync_%s %s", split[0], split[1]);
1728 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1729 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1730 if ((g_strcmp0(split[0], "sh") == 0) ||
1731 (g_strcmp0(split[0], "spawn") == 0)) {
1732 g_free (uzbl.behave.new_window);
1733 uzbl.behave.new_window =
1734 g_strdup_printf("%s %s", split[0], split[1]);
1741 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1746 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1751 if(uzbl.behave.inject_html) {
1752 webkit_web_view_load_html_string (uzbl.gui.web_view,
1753 uzbl.behave.inject_html, NULL);
1762 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1763 uzbl.behave.modmask = 0;
1765 if(uzbl.behave.modkey)
1766 g_free(uzbl.behave.modkey);
1767 uzbl.behave.modkey = buf;
1769 for (i = 0; modkeys[i].key != NULL; i++) {
1770 if (g_strrstr(buf, modkeys[i].key))
1771 uzbl.behave.modmask |= modkeys[i].mask;
1777 if (*uzbl.net.useragent == ' ') {
1778 g_free (uzbl.net.useragent);
1779 uzbl.net.useragent = NULL;
1781 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1782 uzbl.net.useragent, NULL);
1788 gtk_widget_ref(uzbl.gui.scrolled_win);
1789 gtk_widget_ref(uzbl.gui.mainbar);
1790 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1791 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1793 if(uzbl.behave.status_top) {
1794 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1795 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1798 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1799 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1801 gtk_widget_unref(uzbl.gui.scrolled_win);
1802 gtk_widget_unref(uzbl.gui.mainbar);
1803 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1808 set_var_value(gchar *name, gchar *val) {
1809 uzbl_cmdprop *c = NULL;
1813 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1814 if(!c->writeable) return FALSE;
1816 /* check for the variable type */
1817 if (c->type == TYPE_STR) {
1818 buf = expand(val, 0);
1821 } else if(c->type == TYPE_INT) {
1822 int *ip = (int *)c->ptr;
1823 buf = expand(val, 0);
1824 *ip = (int)strtoul(buf, &endp, 10);
1826 } else if (c->type == TYPE_FLOAT) {
1827 float *fp = (float *)c->ptr;
1828 buf = expand(val, 0);
1829 *fp = strtod(buf, &endp);
1833 /* invoke a command specific function */
1834 if(c->func) c->func();
1841 Behaviour *b = &uzbl.behave;
1843 if(b->html_buffer->str) {
1844 webkit_web_view_load_html_string (uzbl.gui.web_view,
1845 b->html_buffer->str, b->base_url);
1846 g_string_free(b->html_buffer, TRUE);
1847 b->html_buffer = g_string_new("");
1851 enum {M_CMD, M_HTML};
1853 parse_cmd_line(const char *ctl_line, GString *result) {
1854 Behaviour *b = &uzbl.behave;
1857 if(b->mode == M_HTML) {
1858 len = strlen(b->html_endmarker);
1859 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1860 if(len == strlen(ctl_line)-1 &&
1861 !strncmp(b->html_endmarker, ctl_line, len)) {
1863 set_var_value("mode", "0");
1868 set_timeout(b->html_timeout);
1869 g_string_append(b->html_buffer, ctl_line);
1872 else if((ctl_line[0] == '#') /* Comments */
1873 || (ctl_line[0] == ' ')
1874 || (ctl_line[0] == '\n'))
1875 ; /* ignore these lines */
1876 else { /* parse a command */
1878 gchar **tokens = NULL;
1879 len = strlen(ctl_line);
1881 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1882 ctlstrip = g_strndup(ctl_line, len - 1);
1883 else ctlstrip = g_strdup(ctl_line);
1885 tokens = g_strsplit(ctlstrip, " ", 2);
1886 parse_command(tokens[0], tokens[1], result);
1893 build_stream_name(int type, const gchar* dir) {
1894 State *s = &uzbl.state;
1898 str = g_strdup_printf
1899 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1900 } else if (type == SOCKET) {
1901 str = g_strdup_printf
1902 ("%s/uzbl_socket_%s", dir, s->instance_name);
1908 control_fifo(GIOChannel *gio, GIOCondition condition) {
1909 if (uzbl.state.verbose)
1910 printf("triggered\n");
1915 if (condition & G_IO_HUP)
1916 g_error ("Fifo: Read end of pipe died!\n");
1919 g_error ("Fifo: GIOChannel broke\n");
1921 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1922 if (ret == G_IO_STATUS_ERROR) {
1923 g_error ("Fifo: Error reading: %s\n", err->message);
1927 parse_cmd_line(ctl_line, NULL);
1934 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1935 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1936 if (unlink(uzbl.comm.fifo_path) == -1)
1937 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1938 g_free(uzbl.comm.fifo_path);
1939 uzbl.comm.fifo_path = NULL;
1942 GIOChannel *chan = NULL;
1943 GError *error = NULL;
1944 gchar *path = build_stream_name(FIFO, dir);
1946 if (!file_exists(path)) {
1947 if (mkfifo (path, 0666) == 0) {
1948 // 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.
1949 chan = g_io_channel_new_file(path, "r+", &error);
1951 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1952 if (uzbl.state.verbose)
1953 printf ("init_fifo: created successfully as %s\n", path);
1954 uzbl.comm.fifo_path = path;
1956 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1957 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1958 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1959 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1961 /* if we got this far, there was an error; cleanup */
1962 if (error) g_error_free (error);
1969 control_stdin(GIOChannel *gio, GIOCondition condition) {
1971 gchar *ctl_line = NULL;
1974 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1975 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1978 parse_cmd_line(ctl_line, NULL);
1986 GIOChannel *chan = NULL;
1987 GError *error = NULL;
1989 chan = g_io_channel_unix_new(fileno(stdin));
1991 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1992 g_error ("Stdin: could not add watch\n");
1994 if (uzbl.state.verbose)
1995 printf ("Stdin: watch added successfully\n");
1998 g_error ("Stdin: Error while opening: %s\n", error->message);
2000 if (error) g_error_free (error);
2004 control_socket(GIOChannel *chan) {
2005 struct sockaddr_un remote;
2006 unsigned int t = sizeof(remote);
2008 GIOChannel *clientchan;
2010 clientsock = accept (g_io_channel_unix_get_fd(chan),
2011 (struct sockaddr *) &remote, &t);
2013 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2014 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2015 (GIOFunc) control_client_socket, clientchan);
2022 control_client_socket(GIOChannel *clientchan) {
2024 GString *result = g_string_new("");
2025 GError *error = NULL;
2029 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2030 if (ret == G_IO_STATUS_ERROR) {
2031 g_warning ("Error reading: %s\n", error->message);
2032 g_io_channel_shutdown(clientchan, TRUE, &error);
2034 } else if (ret == G_IO_STATUS_EOF) {
2035 /* shutdown and remove channel watch from main loop */
2036 g_io_channel_shutdown(clientchan, TRUE, &error);
2041 parse_cmd_line (ctl_line, result);
2042 g_string_append_c(result, '\n');
2043 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2045 if (ret == G_IO_STATUS_ERROR) {
2046 g_warning ("Error writing: %s", error->message);
2048 g_io_channel_flush(clientchan, &error);
2051 if (error) g_error_free (error);
2052 g_string_free(result, TRUE);
2058 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2059 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2060 if (unlink(uzbl.comm.socket_path) == -1)
2061 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2062 g_free(uzbl.comm.socket_path);
2063 uzbl.comm.socket_path = NULL;
2071 GIOChannel *chan = NULL;
2073 struct sockaddr_un local;
2074 gchar *path = build_stream_name(SOCKET, dir);
2076 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2078 local.sun_family = AF_UNIX;
2079 strcpy (local.sun_path, path);
2080 unlink (local.sun_path);
2082 len = strlen (local.sun_path) + sizeof (local.sun_family);
2083 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2084 if (uzbl.state.verbose)
2085 printf ("init_socket: opened in %s\n", path);
2088 if( (chan = g_io_channel_unix_new(sock)) ) {
2089 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2090 uzbl.comm.socket_path = path;
2093 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2095 /* if we got this far, there was an error; cleanup */
2102 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2103 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2105 // this function may be called very early when the templates are not set (yet), hence the checks
2107 update_title (void) {
2108 Behaviour *b = &uzbl.behave;
2111 if (b->show_status) {
2112 if (b->title_format_short) {
2113 parsed = expand(b->title_format_short, 0);
2114 if (uzbl.gui.main_window)
2115 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2118 if (b->status_format) {
2119 parsed = expand(b->status_format, 0);
2120 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2123 if (b->status_background) {
2125 gdk_color_parse (b->status_background, &color);
2126 //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)
2127 if (uzbl.gui.main_window)
2128 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2129 else if (uzbl.gui.plug)
2130 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2133 if (b->title_format_long) {
2134 parsed = expand(b->title_format_long, 0);
2135 if (uzbl.gui.main_window)
2136 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2143 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2147 retreive_geometry();
2152 key_press_cb (GtkWidget* window, GdkEventKey* event)
2154 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2158 if (event->type != GDK_KEY_PRESS ||
2159 event->keyval == GDK_Page_Up ||
2160 event->keyval == GDK_Page_Down ||
2161 event->keyval == GDK_Up ||
2162 event->keyval == GDK_Down ||
2163 event->keyval == GDK_Left ||
2164 event->keyval == GDK_Right ||
2165 event->keyval == GDK_Shift_L ||
2166 event->keyval == GDK_Shift_R)
2169 /* turn off insert mode (if always_insert_mode is not used) */
2170 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2171 set_insert_mode(uzbl.behave.always_insert_mode);
2176 if (uzbl.behave.insert_mode &&
2177 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2178 (!uzbl.behave.modmask)
2183 if (event->keyval == GDK_Escape) {
2186 dehilight(uzbl.gui.web_view, NULL, NULL);
2190 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2191 if (event->keyval == GDK_Insert) {
2193 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2194 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2196 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2199 GString* keycmd = g_string_new(uzbl.state.keycmd);
2200 g_string_append (keycmd, str);
2201 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2208 if (event->keyval == GDK_BackSpace)
2209 keycmd_bs(NULL, NULL, NULL);
2211 gboolean key_ret = FALSE;
2212 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2215 GString* keycmd = g_string_new(uzbl.state.keycmd);
2216 g_string_append(keycmd, event->string);
2217 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2220 run_keycmd(key_ret);
2222 if (key_ret) return (!uzbl.behave.insert_mode);
2227 run_keycmd(const gboolean key_ret) {
2228 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2230 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2232 parse_command(act->name, act->param, NULL);
2236 /* try if it's an incremental keycmd or one that takes args, and run it */
2237 GString* short_keys = g_string_new ("");
2238 GString* short_keys_inc = g_string_new ("");
2240 guint len = strlen(uzbl.state.keycmd);
2241 for (i=0; i<len; i++) {
2242 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2243 g_string_assign(short_keys_inc, short_keys->str);
2244 g_string_append_c(short_keys, '_');
2245 g_string_append_c(short_keys_inc, '*');
2247 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2248 /* run normal cmds only if return was pressed */
2249 exec_paramcmd(act, i);
2252 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2253 if (key_ret) /* just quit the incremental command on return */
2255 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2259 g_string_truncate(short_keys, short_keys->len - 1);
2261 g_string_free (short_keys, TRUE);
2262 g_string_free (short_keys_inc, TRUE);
2266 exec_paramcmd(const Action *act, const guint i) {
2267 GString *parampart = g_string_new (uzbl.state.keycmd);
2268 GString *actionname = g_string_new ("");
2269 GString *actionparam = g_string_new ("");
2270 g_string_erase (parampart, 0, i+1);
2272 g_string_printf (actionname, act->name, parampart->str);
2274 g_string_printf (actionparam, act->param, parampart->str);
2275 parse_command(actionname->str, actionparam->str, NULL);
2276 g_string_free(actionname, TRUE);
2277 g_string_free(actionparam, TRUE);
2278 g_string_free(parampart, TRUE);
2286 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2288 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2289 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2290 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2291 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2292 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2293 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2294 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2295 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2296 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2297 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2298 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2305 g->mainbar = gtk_hbox_new (FALSE, 0);
2307 /* keep a reference to the bar so we can re-pack it at runtime*/
2308 //sbar_ref = g_object_ref(g->mainbar);
2310 g->mainbar_label = gtk_label_new ("");
2311 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2312 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2313 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2314 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2315 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2316 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2322 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2323 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2324 gtk_widget_set_name (window, "Uzbl browser");
2325 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2326 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2327 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2334 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2335 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2336 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2343 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2345 If actname is one that calls an external command, this function will inject
2346 newargs in front of the user-provided args in that command line. They will
2347 come become after the body of the script (in sh) or after the name of
2348 the command to execute (in spawn).
2349 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2350 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2352 The return value consist of two strings: the action (sh, ...) and its args.
2354 If act is not one that calls an external command, then the given action merely
2357 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2358 /* Arrr! Here be memory leaks */
2359 gchar *actdup = g_strdup(actname);
2360 g_array_append_val(rets, actdup);
2362 if ((g_strcmp0(actname, "spawn") == 0) ||
2363 (g_strcmp0(actname, "sh") == 0) ||
2364 (g_strcmp0(actname, "sync_spawn") == 0) ||
2365 (g_strcmp0(actname, "sync_sh") == 0) ||
2366 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2368 GString *a = g_string_new("");
2369 gchar **spawnparts = split_quoted(origargs, FALSE);
2370 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2371 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2373 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2374 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2376 g_array_append_val(rets, a->str);
2377 g_string_free(a, FALSE);
2378 g_strfreev(spawnparts);
2380 gchar *origdup = g_strdup(origargs);
2381 g_array_append_val(rets, origdup);
2383 return (gchar**)g_array_free(rets, FALSE);
2387 run_handler (const gchar *act, const gchar *args) {
2388 /* Consider this code a temporary hack to make the handlers usable.
2389 In practice, all this splicing, injection, and reconstruction is
2390 inefficient, annoying and hard to manage. Potential pitfalls arise
2391 when the handler specific args 1) are not quoted (the handler
2392 callbacks should take care of this) 2) are quoted but interfere
2393 with the users' own quotation. A more ideal solution is
2394 to refactor parse_command so that it doesn't just take a string
2395 and execute it; rather than that, we should have a function which
2396 returns the argument vector parsed from the string. This vector
2397 could be modified (e.g. insert additional args into it) before
2398 passing it to the next function that actually executes it. Though
2399 it still isn't perfect for chain actions.. will reconsider & re-
2400 factor when I have the time. -duc */
2402 char **parts = g_strsplit(act, " ", 2);
2404 if (g_strcmp0(parts[0], "chain") == 0) {
2405 GString *newargs = g_string_new("");
2406 gchar **chainparts = split_quoted(parts[1], FALSE);
2408 /* for every argument in the chain, inject the handler args
2409 and make sure the new parts are wrapped in quotes */
2410 gchar **cp = chainparts;
2412 gchar *quotless = NULL;
2413 gchar **spliced_quotless = NULL; // sigh -_-;
2414 gchar **inpart = NULL;
2417 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2419 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2420 } else quotless = g_strdup(*cp);
2422 spliced_quotless = g_strsplit(quotless, " ", 2);
2423 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2424 g_strfreev(spliced_quotless);
2426 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2432 parse_command(parts[0], &(newargs->str[1]), NULL);
2433 g_string_free(newargs, TRUE);
2434 g_strfreev(chainparts);
2437 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2438 parse_command(inparts[0], inparts[1], NULL);
2446 add_binding (const gchar *key, const gchar *act) {
2447 char **parts = g_strsplit(act, " ", 2);
2454 if (uzbl.state.verbose)
2455 printf ("Binding %-10s : %s\n", key, act);
2456 action = new_action(parts[0], parts[1]);
2458 if (g_hash_table_remove (uzbl.bindings, key))
2459 g_warning ("Overwriting existing binding for \"%s\"", key);
2460 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2465 get_xdg_var (XDG_Var xdg) {
2466 const gchar* actual_value = getenv (xdg.environmental);
2467 const gchar* home = getenv ("HOME");
2468 gchar* return_value;
2470 if (! actual_value || strcmp (actual_value, "") == 0) {
2471 if (xdg.default_value) {
2472 return_value = str_replace ("~", home, xdg.default_value);
2474 return_value = NULL;
2477 return_value = str_replace("~", home, actual_value);
2480 return return_value;
2484 find_xdg_file (int xdg_type, char* filename) {
2485 /* xdg_type = 0 => config
2486 xdg_type = 1 => data
2487 xdg_type = 2 => cache*/
2489 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2490 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2493 gchar* temporary_string;
2497 if (! file_exists (temporary_file) && xdg_type != 2) {
2498 buf = get_xdg_var (XDG[3 + xdg_type]);
2499 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2502 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2503 g_free (temporary_file);
2504 temporary_file = g_strconcat (temporary_string, filename, NULL);
2508 //g_free (temporary_string); - segfaults.
2510 if (file_exists (temporary_file)) {
2511 return temporary_file;
2518 State *s = &uzbl.state;
2519 Network *n = &uzbl.net;
2521 for (i = 0; default_config[i].command != NULL; i++) {
2522 parse_cmd_line(default_config[i].command, NULL);
2525 if (g_strcmp0(s->config_file, "-") == 0) {
2526 s->config_file = NULL;
2530 else if (!s->config_file) {
2531 s->config_file = find_xdg_file (0, "/uzbl/config");
2534 if (s->config_file) {
2535 GArray* lines = read_file_by_line (s->config_file);
2539 while ((line = g_array_index(lines, gchar*, i))) {
2540 parse_cmd_line (line, NULL);
2544 g_array_free (lines, TRUE);
2546 if (uzbl.state.verbose)
2547 printf ("No configuration file loaded.\n");
2550 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2553 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2556 if (!uzbl.behave.cookie_handler)
2559 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2560 GString *s = g_string_new ("");
2561 SoupURI * soup_uri = soup_message_get_uri(msg);
2562 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2563 run_handler(uzbl.behave.cookie_handler, s->str);
2565 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2566 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2567 if ( p != NULL ) *p = '\0';
2568 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2570 if (uzbl.comm.sync_stdout)
2571 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2573 g_string_free(s, TRUE);
2577 save_cookies (SoupMessage *msg, gpointer user_data){
2581 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2582 cookie = soup_cookie_to_set_cookie_header(ck->data);
2583 SoupURI * soup_uri = soup_message_get_uri(msg);
2584 GString *s = g_string_new ("");
2585 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2586 run_handler(uzbl.behave.cookie_handler, s->str);
2588 g_string_free(s, TRUE);
2593 /* --- WEBINSPECTOR --- */
2595 hide_window_cb(GtkWidget *widget, gpointer data) {
2598 gtk_widget_hide(widget);
2602 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2605 (void) web_inspector;
2606 GtkWidget* scrolled_window;
2607 GtkWidget* new_web_view;
2610 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2611 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2612 G_CALLBACK(hide_window_cb), NULL);
2614 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2615 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2616 gtk_widget_show(g->inspector_window);
2618 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2619 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2620 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2621 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2622 gtk_widget_show(scrolled_window);
2624 new_web_view = webkit_web_view_new();
2625 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2627 return WEBKIT_WEB_VIEW(new_web_view);
2631 inspector_show_window_cb (WebKitWebInspector* inspector){
2633 gtk_widget_show(uzbl.gui.inspector_window);
2637 /* TODO: Add variables and code to make use of these functions */
2639 inspector_close_window_cb (WebKitWebInspector* inspector){
2645 inspector_attach_window_cb (WebKitWebInspector* inspector){
2651 inspector_detach_window_cb (WebKitWebInspector* inspector){
2657 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2663 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2669 set_up_inspector() {
2671 WebKitWebSettings *settings = view_settings();
2672 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2674 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2675 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2676 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2677 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2678 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2679 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2680 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2682 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2686 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2688 uzbl_cmdprop *c = v;
2693 if(c->type == TYPE_STR)
2694 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2695 else if(c->type == TYPE_INT)
2696 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2697 else if(c->type == TYPE_FLOAT)
2698 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2702 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2706 printf("bind %s = %s %s\n", (char *)k ,
2707 (char *)a->name, a->param?(char *)a->param:"");
2712 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2713 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2717 retreive_geometry() {
2719 GString *buf = g_string_new("");
2721 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2722 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2724 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2726 if(uzbl.gui.geometry)
2727 g_free(uzbl.gui.geometry);
2728 uzbl.gui.geometry = g_string_free(buf, FALSE);
2731 /* set up gtk, gobject, variable defaults and other things that tests and other
2732 * external applications need to do anyhow */
2734 initialize(int argc, char *argv[]) {
2735 if (!g_thread_supported ())
2736 g_thread_init (NULL);
2737 uzbl.state.executable_path = g_strdup(argv[0]);
2738 uzbl.state.selected_url = NULL;
2739 uzbl.state.searchtx = NULL;
2741 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2742 g_option_context_add_main_entries (context, entries, NULL);
2743 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2744 g_option_context_parse (context, &argc, &argv, NULL);
2745 g_option_context_free(context);
2747 if (uzbl.behave.print_version) {
2748 printf("Commit: %s\n", COMMIT);
2752 /* initialize hash table */
2753 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2755 uzbl.net.soup_session = webkit_get_default_session();
2756 uzbl.state.keycmd = g_strdup("");
2758 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2759 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2760 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2761 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2762 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2763 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2765 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2766 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2767 uzbl.gui.sbar.progress_w = 10;
2769 /* HTML mode defaults*/
2770 uzbl.behave.html_buffer = g_string_new("");
2771 uzbl.behave.html_endmarker = g_strdup(".");
2772 uzbl.behave.html_timeout = 60;
2773 uzbl.behave.base_url = g_strdup("http://invalid");
2775 /* default mode indicators */
2776 uzbl.behave.insert_indicator = g_strdup("I");
2777 uzbl.behave.cmd_indicator = g_strdup("C");
2779 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2780 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2781 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2782 uzbl.info.arch = ARCH;
2783 uzbl.info.commit = COMMIT;
2786 make_var_to_name_hash();
2791 #ifndef UZBL_LIBRARY
2794 main (int argc, char* argv[]) {
2795 initialize(argc, argv);
2797 gtk_init (&argc, &argv);
2799 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2800 //main_window_ref = g_object_ref(scrolled_window);
2801 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2802 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2804 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2805 GTK_WIDGET (uzbl.gui.web_view));
2807 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2811 /* initial packing */
2812 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2813 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2815 if (uzbl.state.socket_id) {
2816 uzbl.gui.plug = create_plug ();
2817 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2818 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2820 uzbl.gui.main_window = create_window ();
2821 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2822 gtk_widget_show_all (uzbl.gui.main_window);
2823 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2826 if(!uzbl.state.instance_name)
2827 uzbl.state.instance_name = itos((int)uzbl.xwin);
2829 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2831 if (uzbl.state.verbose) {
2832 printf("Uzbl start location: %s\n", argv[0]);
2833 if (uzbl.state.socket_id)
2834 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2836 printf("window_id %i\n",(int) uzbl.xwin);
2837 printf("pid %i\n", getpid ());
2838 printf("name: %s\n", uzbl.state.instance_name);
2841 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2842 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2843 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2844 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2845 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2847 if(uzbl.gui.geometry)
2850 retreive_geometry();
2852 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2853 if (argc > 1 && !uzbl.state.uri)
2854 uri_override = g_strdup(argv[1]);
2855 gboolean verbose_override = uzbl.state.verbose;
2858 set_insert_mode(FALSE);
2860 if (!uzbl.behave.show_status)
2861 gtk_widget_hide(uzbl.gui.mainbar);
2868 if (verbose_override > uzbl.state.verbose)
2869 uzbl.state.verbose = verbose_override;
2872 set_var_value("uri", uri_override);
2873 g_free(uri_override);
2874 } else if (uzbl.state.uri)
2875 cmd_load_uri(uzbl.gui.web_view, NULL);
2880 return EXIT_SUCCESS;
2884 /* vi: set et ts=4: */