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>
59 #include <sys/ioctl.h>
65 /* commandline arguments (set initial values for the state variables) */
67 GOptionEntry entries[] =
69 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
70 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
71 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
72 "Whether to print all messages or just errors.", NULL },
73 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
74 "Name of the current instance (defaults to Xorg window id)", "NAME" },
75 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
76 "Path to config file or '-' for stdin", "FILE" },
77 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
78 "Socket ID", "SOCKET" },
79 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
80 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
81 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
82 "Print the version and exit", NULL },
83 { NULL, 0, 0, 0, NULL, NULL, NULL }
86 /* associate command names to their properties */
87 typedef const struct {
88 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
89 the PTR() macro is kind of preventing this change at the moment. */
97 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
99 /* abbreviations to help keep the table's width humane */
100 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
101 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
106 } var_name_to_ptr[] = {
107 /* variable name pointer to variable in code type dump callback function */
108 /* ---------------------------------------------------------------------------------------------- */
109 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
110 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
111 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
112 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
113 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
114 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
115 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
116 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
117 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
118 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
119 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
120 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
121 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
122 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
123 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
124 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
125 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
126 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
127 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
128 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
129 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
130 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
131 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
132 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
133 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
134 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
135 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
136 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
137 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
138 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
139 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
140 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
141 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
142 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
143 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
144 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
145 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
146 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
147 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
148 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
150 /* exported WebKitWebSettings properties */
151 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
152 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
153 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
154 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
155 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
156 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
157 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
158 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
159 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
160 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
161 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
162 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
163 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
164 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
165 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
166 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
168 /* constants (not dumpable or writeable) */
169 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
170 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
171 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
172 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
173 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
174 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
175 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
176 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
177 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
178 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
179 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
181 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
182 }, *n2v_p = var_name_to_ptr;
189 { "SHIFT", GDK_SHIFT_MASK }, // shift
190 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
191 { "CONTROL", GDK_CONTROL_MASK }, // control
192 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
193 { "MOD2", GDK_MOD2_MASK }, // 5th mod
194 { "MOD3", GDK_MOD3_MASK }, // 6th mod
195 { "MOD4", GDK_MOD4_MASK }, // 7th mod
196 { "MOD5", GDK_MOD5_MASK }, // 8th mod
197 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
198 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
199 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
200 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
201 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
202 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
203 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
204 { "META", GDK_META_MASK }, // meta (since 2.10)
209 /* construct a hash from the var_name_to_ptr array for quick access */
211 make_var_to_name_hash() {
212 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
214 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
219 /* --- UTILITY FUNCTIONS --- */
220 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
222 get_exp_type(gchar *s) {
226 else if(*(s+1) == '{')
227 return EXP_BRACED_VAR;
228 else if(*(s+1) == '<')
230 else if(*(s+1) == '[')
233 return EXP_SIMPLE_VAR;
239 * recurse == 1: don't expand '@(command)@'
240 * recurse == 2: don't expand '@<java script>@'
243 expand(char *s, guint recurse) {
247 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
252 gchar *cmd_stdout = NULL;
254 GString *buf = g_string_new("");
255 GString *js_ret = g_string_new("");
260 g_string_append_c(buf, *++s);
265 etype = get_exp_type(s);
270 vend = strpbrk(s, end_simple_var);
271 if(!vend) vend = strchr(s, '\0');
275 vend = strchr(s, upto);
276 if(!vend) vend = strchr(s, '\0');
280 strcpy(str_end, ")@");
282 vend = strstr(s, str_end);
283 if(!vend) vend = strchr(s, '\0');
287 strcpy(str_end, ">@");
289 vend = strstr(s, str_end);
290 if(!vend) vend = strchr(s, '\0');
294 strcpy(str_end, "]@");
296 vend = strstr(s, str_end);
297 if(!vend) vend = strchr(s, '\0');
302 strncpy(ret, s, vend-s);
306 if(etype == EXP_SIMPLE_VAR ||
307 etype == EXP_BRACED_VAR) {
308 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
309 if(c->type == TYPE_STR && *c->ptr != NULL) {
310 g_string_append(buf, (gchar *)*c->ptr);
311 } else if(c->type == TYPE_INT) {
312 g_string_append_printf(buf, "%d", (int)*c->ptr);
314 else if(c->type == TYPE_FLOAT) {
315 g_string_append_printf(buf, "%f", *(float *)c->ptr);
319 if(etype == EXP_SIMPLE_VAR)
324 else if(recurse != 1 &&
326 mycmd = expand(ret, 1);
327 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
331 g_printerr("error on running command: %s\n", err->message);
334 else if (*cmd_stdout) {
335 int len = strlen(cmd_stdout);
337 if(cmd_stdout[len-1] == '\n')
338 cmd_stdout[--len] = 0; /* strip trailing newline */
340 g_string_append(buf, cmd_stdout);
345 else if(recurse != 2 &&
347 mycmd = expand(ret, 2);
348 eval_js(uzbl.gui.web_view, mycmd, js_ret);
352 g_string_append(buf, js_ret->str);
353 g_string_free(js_ret, TRUE);
354 js_ret = g_string_new("");
358 else if(etype == EXP_ESCAPE) {
359 mycmd = expand(ret, 0);
360 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
362 g_string_append(buf, escaped);
371 g_string_append_c(buf, *s);
376 g_string_free(js_ret, TRUE);
377 return g_string_free(buf, FALSE);
384 snprintf(tmp, sizeof(tmp), "%i", val);
385 return g_strdup(tmp);
389 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
392 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
395 str_replace (const char* search, const char* replace, const char* string) {
399 buf = g_strsplit (string, search, -1);
400 ret = g_strjoinv (replace, buf);
401 g_strfreev(buf); // somebody said this segfaults
407 read_file_by_line (gchar *path) {
408 GIOChannel *chan = NULL;
409 gchar *readbuf = NULL;
411 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
414 chan = g_io_channel_new_file(path, "r", NULL);
417 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
418 const gchar* val = g_strdup (readbuf);
419 g_array_append_val (lines, val);
424 g_io_channel_unref (chan);
426 fprintf(stderr, "File '%s' not be read.\n", path);
433 parseenv (char* string) {
434 extern char** environ;
435 gchar* tmpstr = NULL;
439 while (environ[i] != NULL) {
440 gchar** env = g_strsplit (environ[i], "=", 2);
441 gchar* envname = g_strconcat ("$", env[0], NULL);
443 if (g_strrstr (string, envname) != NULL) {
444 tmpstr = g_strdup(string);
446 string = str_replace(envname, env[1], tmpstr);
451 g_strfreev (env); // somebody said this breaks uzbl
459 setup_signal(int signr, sigfunc *shandler) {
460 struct sigaction nh, oh;
462 nh.sa_handler = shandler;
463 sigemptyset(&nh.sa_mask);
466 if(sigaction(signr, &nh, &oh) < 0)
474 if (uzbl.behave.fifo_dir)
475 unlink (uzbl.comm.fifo_path);
476 if (uzbl.behave.socket_dir)
477 unlink (uzbl.comm.socket_path);
479 g_free(uzbl.state.executable_path);
480 g_free(uzbl.state.keycmd);
481 g_hash_table_destroy(uzbl.bindings);
482 g_hash_table_destroy(uzbl.behave.commands);
485 /* used for html_mode_timeout
486 * be sure to extend this function to use
487 * more timers if needed in other places
490 set_timeout(int seconds) {
492 memset(&t, 0, sizeof t);
494 t.it_value.tv_sec = seconds;
495 t.it_value.tv_usec = 0;
496 setitimer(ITIMER_REAL, &t, NULL);
499 /* --- SIGNAL HANDLER --- */
502 catch_sigterm(int s) {
508 catch_sigint(int s) {
518 set_var_value("mode", "0");
523 /* --- CALLBACKS --- */
526 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
529 (void) navigation_action;
530 (void) policy_decision;
532 const gchar* uri = webkit_network_request_get_uri (request);
533 if (uzbl.state.verbose)
534 printf("New window requested -> %s \n", uri);
535 webkit_web_policy_decision_use(policy_decision);
540 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
545 /* If we can display it, let's display it... */
546 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
547 webkit_web_policy_decision_use (policy_decision);
551 /* ...everything we can't displayed is downloaded */
552 webkit_web_policy_decision_download (policy_decision);
557 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
561 if (uzbl.state.selected_url != NULL) {
562 if (uzbl.state.verbose)
563 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
564 new_window_load_uri(uzbl.state.selected_url);
566 if (uzbl.state.verbose)
567 printf("New web view -> %s\n","Nothing to open, exiting");
573 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
576 if (uzbl.behave.download_handler) {
577 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
578 if (uzbl.state.verbose)
579 printf("Download -> %s\n",uri);
580 /* if urls not escaped, we may have to escape and quote uri before this call */
581 run_handler(uzbl.behave.download_handler, uri);
586 /* scroll a bar in a given direction */
588 scroll (GtkAdjustment* bar, GArray *argv) {
592 gdouble page_size = gtk_adjustment_get_page_size(bar);
593 gdouble value = gtk_adjustment_get_value(bar);
594 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
597 value += page_size * amount * 0.01;
601 max_value = gtk_adjustment_get_upper(bar) - page_size;
603 if (value > max_value)
604 value = max_value; /* don't scroll past the end of the page */
606 gtk_adjustment_set_value (bar, value);
610 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
611 (void) page; (void) argv; (void) result;
612 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
616 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) argv; (void) result;
618 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
619 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
623 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
624 (void) page; (void) result;
625 scroll(uzbl.gui.bar_v, argv);
629 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
630 (void) page; (void) result;
631 scroll(uzbl.gui.bar_h, argv);
636 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
637 if(uzbl.state.verbose)
638 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
640 /* update geometry var with the actual geometry
641 this is necessary as some WMs don't seem to honour
642 the above setting and we don't want to end up with
643 wrong geometry information
650 if (!uzbl.behave.show_status) {
651 gtk_widget_hide(uzbl.gui.mainbar);
653 gtk_widget_show(uzbl.gui.mainbar);
659 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
664 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
668 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
673 if (uzbl.behave.show_status) {
674 gtk_widget_hide(uzbl.gui.mainbar);
676 gtk_widget_show(uzbl.gui.mainbar);
678 uzbl.behave.show_status = !uzbl.behave.show_status;
683 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
687 //Set selected_url state variable
688 g_free(uzbl.state.selected_url);
689 uzbl.state.selected_url = NULL;
691 uzbl.state.selected_url = g_strdup(link);
697 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
700 const gchar *title = webkit_web_view_get_title(web_view);
701 if (uzbl.gui.main_title)
702 g_free (uzbl.gui.main_title);
703 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
708 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
711 uzbl.gui.sbar.load_progress = progress;
713 g_free(uzbl.gui.sbar.progress_bar);
714 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
720 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
724 if (uzbl.behave.load_finish_handler)
725 run_handler(uzbl.behave.load_finish_handler, "");
728 void clear_keycmd() {
729 g_free(uzbl.state.keycmd);
730 uzbl.state.keycmd = g_strdup("");
734 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
738 uzbl.gui.sbar.load_progress = 0;
739 clear_keycmd(); // don't need old commands to remain on new page?
740 if (uzbl.behave.load_start_handler)
741 run_handler(uzbl.behave.load_start_handler, "");
745 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
748 g_free (uzbl.state.uri);
749 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
750 uzbl.state.uri = g_string_free (newuri, FALSE);
751 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
752 set_insert_mode(uzbl.behave.always_insert_mode);
755 if (uzbl.behave.load_commit_handler)
756 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
760 destroy_cb (GtkWidget* widget, gpointer data) {
768 if (uzbl.behave.history_handler) {
770 struct tm * timeinfo;
773 timeinfo = localtime ( &rawtime );
774 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
775 run_handler(uzbl.behave.history_handler, date);
780 /* VIEW funcs (little webkit wrappers) */
781 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
783 VIEWFUNC(reload_bypass_cache)
784 VIEWFUNC(stop_loading)
791 /* -- command to callback/function map for things we cannot attach to any signals */
792 struct {char *key; CommandInfo value;} cmdlist[] =
793 { /* key function no_split */
794 { "back", {view_go_back, 0} },
795 { "forward", {view_go_forward, 0} },
796 { "scroll_vert", {scroll_vert, 0} },
797 { "scroll_horz", {scroll_horz, 0} },
798 { "scroll_begin", {scroll_begin, 0} },
799 { "scroll_end", {scroll_end, 0} },
800 { "reload", {view_reload, 0}, },
801 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
802 { "stop", {view_stop_loading, 0}, },
803 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
804 { "zoom_out", {view_zoom_out, 0}, },
805 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
806 { "uri", {load_uri, TRUE} },
807 { "js", {run_js, TRUE} },
808 { "script", {run_external_js, 0} },
809 { "toggle_status", {toggle_status_cb, 0} },
810 { "spawn", {spawn, 0} },
811 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
812 { "sh", {spawn_sh, 0} },
813 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
814 { "talk_to_socket", {talk_to_socket, 0} },
815 { "exit", {close_uzbl, 0} },
816 { "search", {search_forward_text, TRUE} },
817 { "search_reverse", {search_reverse_text, TRUE} },
818 { "dehilight", {dehilight, 0} },
819 { "toggle_insert_mode", {toggle_insert_mode, 0} },
820 { "set", {set_var, TRUE} },
821 //{ "get", {get_var, TRUE} },
822 { "bind", {act_bind, TRUE} },
823 { "dump_config", {act_dump_config, 0} },
824 { "keycmd", {keycmd, TRUE} },
825 { "keycmd_nl", {keycmd_nl, TRUE} },
826 { "keycmd_bs", {keycmd_bs, 0} },
827 { "chain", {chain, 0} },
828 { "print", {print, TRUE} }
835 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
837 for (i = 0; i < LENGTH(cmdlist); i++)
838 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
841 /* -- CORE FUNCTIONS -- */
844 free_action(gpointer act) {
845 Action *action = (Action*)act;
846 g_free(action->name);
848 g_free(action->param);
853 new_action(const gchar *name, const gchar *param) {
854 Action *action = g_new(Action, 1);
856 action->name = g_strdup(name);
858 action->param = g_strdup(param);
860 action->param = NULL;
866 file_exists (const char * filename) {
867 return (access(filename, F_OK) == 0);
871 set_var(WebKitWebView *page, GArray *argv, GString *result) {
872 (void) page; (void) result;
873 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
874 if (split[0] != NULL) {
875 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
876 set_var_value(g_strstrip(split[0]), value);
883 print(WebKitWebView *page, GArray *argv, GString *result) {
884 (void) page; (void) result;
887 buf = expand(argv_idx(argv, 0), 0);
888 g_string_assign(result, buf);
893 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
894 (void) page; (void) result;
895 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
896 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
897 add_binding(g_strstrip(split[0]), value);
915 set_mode_indicator() {
916 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
917 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
922 set_mode_indicator();
927 set_insert_mode(gboolean mode) {
928 uzbl.behave.insert_mode = mode;
929 set_mode_indicator();
933 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
934 (void) page; (void) result;
936 if (argv_idx(argv, 0)) {
937 if (strcmp (argv_idx(argv, 0), "0") == 0) {
938 set_insert_mode(FALSE);
940 set_insert_mode(TRUE);
943 set_insert_mode( !uzbl.behave.insert_mode );
950 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
953 if (argv_idx(argv, 0)) {
954 GString* newuri = g_string_new (argv_idx(argv, 0));
955 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
956 run_js(web_view, argv, NULL);
959 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
960 g_string_prepend (newuri, "http://");
961 /* if we do handle cookies, ask our handler for them */
962 webkit_web_view_load_uri (web_view, newuri->str);
963 g_string_free (newuri, TRUE);
970 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
971 size_t argumentCount, const JSValueRef arguments[],
972 JSValueRef* exception) {
977 JSStringRef js_result_string;
978 GString *result = g_string_new("");
980 if (argumentCount >= 1) {
981 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
982 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
983 char ctl_line[arg_size];
984 JSStringGetUTF8CString(arg, ctl_line, arg_size);
986 parse_cmd_line(ctl_line, result);
988 JSStringRelease(arg);
990 js_result_string = JSStringCreateWithUTF8CString(result->str);
992 g_string_free(result, TRUE);
994 return JSValueMakeString(ctx, js_result_string);
997 JSStaticFunction js_static_functions[] = {
998 {"run", js_run_command, kJSPropertyAttributeNone},
1003 /* This function creates the class and its definition, only once */
1004 if (!uzbl.js.initialized) {
1005 /* it would be pretty cool to make this dynamic */
1006 uzbl.js.classdef = kJSClassDefinitionEmpty;
1007 uzbl.js.classdef.staticFunctions = js_static_functions;
1009 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1015 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1016 WebKitWebFrame *frame;
1017 JSGlobalContextRef context;
1018 JSObjectRef globalobject;
1019 JSStringRef var_name;
1021 JSStringRef js_script;
1022 JSValueRef js_result;
1023 JSStringRef js_result_string;
1024 size_t js_result_size;
1028 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1029 context = webkit_web_frame_get_global_context(frame);
1030 globalobject = JSContextGetGlobalObject(context);
1032 /* uzbl javascript namespace */
1033 var_name = JSStringCreateWithUTF8CString("Uzbl");
1034 JSObjectSetProperty(context, globalobject, var_name,
1035 JSObjectMake(context, uzbl.js.classref, NULL),
1036 kJSClassAttributeNone, NULL);
1038 /* evaluate the script and get return value*/
1039 js_script = JSStringCreateWithUTF8CString(script);
1040 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1041 if (js_result && !JSValueIsUndefined(context, js_result)) {
1042 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1043 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1045 if (js_result_size) {
1046 char js_result_utf8[js_result_size];
1047 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1048 g_string_assign(result, js_result_utf8);
1051 JSStringRelease(js_result_string);
1055 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1057 JSStringRelease(var_name);
1058 JSStringRelease(js_script);
1062 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1063 if (argv_idx(argv, 0))
1064 eval_js(web_view, argv_idx(argv, 0), result);
1068 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1070 if (argv_idx(argv, 0)) {
1071 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1076 while ((line = g_array_index(lines, gchar*, i))) {
1078 js = g_strdup (line);
1080 gchar* newjs = g_strconcat (js, line, NULL);
1087 if (uzbl.state.verbose)
1088 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1090 if (argv_idx (argv, 1)) {
1091 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1095 eval_js (web_view, js, result);
1097 g_array_free (lines, TRUE);
1102 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1103 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1104 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1105 webkit_web_view_unmark_text_matches (page);
1106 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1107 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1111 if (uzbl.state.searchtx) {
1112 if (uzbl.state.verbose)
1113 printf ("Searching: %s\n", uzbl.state.searchtx);
1114 webkit_web_view_set_highlight_text_matches (page, TRUE);
1115 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1120 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1122 search_text(page, argv, TRUE);
1126 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1128 search_text(page, argv, FALSE);
1132 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1133 (void) argv; (void) result;
1134 webkit_web_view_set_highlight_text_matches (page, FALSE);
1139 new_window_load_uri (const gchar * uri) {
1140 if (uzbl.behave.new_window) {
1141 GString *s = g_string_new ("");
1142 g_string_printf(s, "'%s'", uri);
1143 run_handler(uzbl.behave.new_window, s->str);
1146 GString* to_execute = g_string_new ("");
1147 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1149 for (i = 0; entries[i].long_name != NULL; i++) {
1150 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1151 gchar** str = (gchar**)entries[i].arg_data;
1153 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1157 if (uzbl.state.verbose)
1158 printf("\n%s\n", to_execute->str);
1159 g_spawn_command_line_async (to_execute->str, NULL);
1160 g_string_free (to_execute, TRUE);
1164 chain (WebKitWebView *page, GArray *argv, GString *result) {
1165 (void) page; (void) result;
1167 gchar **parts = NULL;
1169 while ((a = argv_idx(argv, i++))) {
1170 parts = g_strsplit (a, " ", 2);
1172 parse_command(parts[0], parts[1], result);
1178 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1182 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1188 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1192 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1198 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1203 int len = strlen(uzbl.state.keycmd);
1204 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1206 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1211 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1218 /* --Statusbar functions-- */
1220 build_progressbar_ascii(int percent) {
1221 int width=uzbl.gui.sbar.progress_w;
1224 GString *bar = g_string_new("");
1226 l = (double)percent*((double)width/100.);
1227 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1229 for(i=0; i<(int)l; i++)
1230 g_string_append(bar, uzbl.gui.sbar.progress_s);
1233 g_string_append(bar, uzbl.gui.sbar.progress_u);
1235 return g_string_free(bar, FALSE);
1237 /* --End Statusbar functions-- */
1240 sharg_append(GArray *a, const gchar *str) {
1241 const gchar *s = (str ? str : "");
1242 g_array_append_val(a, s);
1245 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1247 run_command (const gchar *command, const guint npre, const gchar **args,
1248 const gboolean sync, char **output_stdout) {
1249 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1252 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1253 gchar *pid = itos(getpid());
1254 gchar *xwin = itos(uzbl.xwin);
1256 sharg_append(a, command);
1257 for (i = 0; i < npre; i++) /* add n args before the default vars */
1258 sharg_append(a, args[i]);
1259 sharg_append(a, uzbl.state.config_file);
1260 sharg_append(a, pid);
1261 sharg_append(a, xwin);
1262 sharg_append(a, uzbl.comm.fifo_path);
1263 sharg_append(a, uzbl.comm.socket_path);
1264 sharg_append(a, uzbl.state.uri);
1265 sharg_append(a, uzbl.gui.main_title);
1267 for (i = npre; i < g_strv_length((gchar**)args); i++)
1268 sharg_append(a, args[i]);
1272 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1274 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1275 NULL, NULL, output_stdout, NULL, NULL, &err);
1276 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1277 NULL, NULL, NULL, &err);
1279 if (uzbl.state.verbose) {
1280 GString *s = g_string_new("spawned:");
1281 for (i = 0; i < (a->len); i++) {
1282 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1283 g_string_append_printf(s, " %s", qarg);
1286 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1287 printf("%s\n", s->str);
1288 g_string_free(s, TRUE);
1290 printf("Stdout: %s\n", *output_stdout);
1294 g_printerr("error on run_command: %s\n", err->message);
1299 g_array_free (a, TRUE);
1304 split_quoted(const gchar* src, const gboolean unquote) {
1305 /* split on unquoted space, return array of strings;
1306 remove a layer of quotes and backslashes if unquote */
1307 if (!src) return NULL;
1309 gboolean dq = FALSE;
1310 gboolean sq = FALSE;
1311 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1312 GString *s = g_string_new ("");
1316 for (p = src; *p != '\0'; p++) {
1317 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1318 else if (*p == '\\') { g_string_append_c(s, *p++);
1319 g_string_append_c(s, *p); }
1320 else if ((*p == '"') && unquote && !sq) dq = !dq;
1321 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1323 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1324 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1326 else if ((*p == ' ') && !dq && !sq) {
1327 dup = g_strdup(s->str);
1328 g_array_append_val(a, dup);
1329 g_string_truncate(s, 0);
1330 } else g_string_append_c(s, *p);
1332 dup = g_strdup(s->str);
1333 g_array_append_val(a, dup);
1334 ret = (gchar**)a->data;
1335 g_array_free (a, FALSE);
1336 g_string_free (s, TRUE);
1341 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1342 (void)web_view; (void)result;
1343 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1344 if (argv_idx(argv, 0))
1345 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1349 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1350 (void)web_view; (void)result;
1352 if (argv_idx(argv, 0))
1353 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1354 TRUE, &uzbl.comm.sync_stdout);
1358 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1359 (void)web_view; (void)result;
1360 if (!uzbl.behave.shell_cmd) {
1361 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1366 gchar *spacer = g_strdup("");
1367 g_array_insert_val(argv, 1, spacer);
1368 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1370 for (i = 1; i < g_strv_length(cmd); i++)
1371 g_array_prepend_val(argv, cmd[i]);
1373 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1379 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1380 (void)web_view; (void)result;
1381 if (!uzbl.behave.shell_cmd) {
1382 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1387 gchar *spacer = g_strdup("");
1388 g_array_insert_val(argv, 1, spacer);
1389 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1391 for (i = 1; i < g_strv_length(cmd); i++)
1392 g_array_prepend_val(argv, cmd[i]);
1394 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1395 TRUE, &uzbl.comm.sync_stdout);
1401 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1402 (void)web_view; (void)result;
1405 struct sockaddr_un sa;
1412 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1414 /* This function could be optimised by storing a hash table of socket paths
1415 and associated connected file descriptors rather than closing and
1416 re-opening for every call. Also we could launch a script if socket connect
1419 /* First element argv[0] is path to socket. Following elements are tokens to
1420 write to the socket. We write them as a single packet with each token
1421 separated by an ASCII nul (\0). */
1423 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1428 /* copy socket path, null terminate result */
1429 sockpath = g_array_index(argv, char*, 0);
1430 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1431 sa.sun_family = AF_UNIX;
1433 /* create socket file descriptor and connect it to path */
1434 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1436 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1439 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1440 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1445 /* build request vector */
1446 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1448 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1452 for(i = 1; i < argv->len; ++i) {
1453 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1454 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1458 ret = writev(fd, iov, argv->len - 1);
1461 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1466 /* wait for a response, with a 500ms timeout */
1468 pfd.events = POLLIN;
1470 ret = poll(&pfd, 1, 500);
1472 if(ret == 0) errno = ETIMEDOUT;
1473 if(errno == EINTR) continue;
1474 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1480 /* get length of response */
1481 if(ioctl(fd, FIONREAD, &len) == -1) {
1482 g_printerr("talk_to_socket: cannot find daemon response length, "
1483 "ioctl failed (%s)\n", strerror(errno));
1488 /* if there is a response, read it */
1490 uzbl.comm.sync_stdout = g_malloc(len + 1);
1491 if(!uzbl.comm.sync_stdout) {
1492 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1496 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1498 ret = read(fd, uzbl.comm.sync_stdout, len);
1500 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1513 parse_command(const char *cmd, const char *param, GString *result) {
1516 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1518 gchar **par = split_quoted(param, TRUE);
1519 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1521 if (c->no_split) { /* don't split */
1522 sharg_append(a, param);
1524 for (i = 0; i < g_strv_length(par); i++)
1525 sharg_append(a, par[i]);
1528 if (result == NULL) {
1529 GString *result_print = g_string_new("");
1531 c->function(uzbl.gui.web_view, a, result_print);
1532 if (result_print->len)
1533 printf("%*s\n", result_print->len, result_print->str);
1535 g_string_free(result_print, TRUE);
1537 c->function(uzbl.gui.web_view, a, result);
1540 g_array_free (a, TRUE);
1543 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1550 if(*uzbl.net.proxy_url == ' '
1551 || uzbl.net.proxy_url == NULL) {
1552 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1553 (GType) SOUP_SESSION_PROXY_URI);
1556 suri = soup_uri_new(uzbl.net.proxy_url);
1557 g_object_set(G_OBJECT(uzbl.net.soup_session),
1558 SOUP_SESSION_PROXY_URI,
1560 soup_uri_free(suri);
1567 if(file_exists(uzbl.gui.icon)) {
1568 if (uzbl.gui.main_window)
1569 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1571 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1577 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1578 g_array_append_val (a, uzbl.state.uri);
1579 load_uri(uzbl.gui.web_view, a, NULL);
1580 g_array_free (a, TRUE);
1584 cmd_always_insert_mode() {
1585 set_insert_mode(uzbl.behave.always_insert_mode);
1591 g_object_set(G_OBJECT(uzbl.net.soup_session),
1592 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1596 cmd_max_conns_host() {
1597 g_object_set(G_OBJECT(uzbl.net.soup_session),
1598 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1603 soup_session_remove_feature
1604 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1605 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1606 /*g_free(uzbl.net.soup_logger);*/
1608 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1609 soup_session_add_feature(uzbl.net.soup_session,
1610 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1615 return webkit_web_view_get_settings(uzbl.gui.web_view);
1620 WebKitWebSettings *ws = view_settings();
1621 if (uzbl.behave.font_size > 0) {
1622 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1625 if (uzbl.behave.monospace_size > 0) {
1626 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1627 uzbl.behave.monospace_size, NULL);
1629 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1630 uzbl.behave.font_size, NULL);
1636 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1640 cmd_disable_plugins() {
1641 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1642 !uzbl.behave.disable_plugins, NULL);
1646 cmd_disable_scripts() {
1647 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1648 !uzbl.behave.disable_scripts, NULL);
1652 cmd_minimum_font_size() {
1653 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1654 uzbl.behave.minimum_font_size, NULL);
1657 cmd_autoload_img() {
1658 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1659 uzbl.behave.autoload_img, NULL);
1664 cmd_autoshrink_img() {
1665 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1666 uzbl.behave.autoshrink_img, NULL);
1671 cmd_enable_spellcheck() {
1672 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1673 uzbl.behave.enable_spellcheck, NULL);
1677 cmd_enable_private() {
1678 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1679 uzbl.behave.enable_private, NULL);
1684 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1685 uzbl.behave.print_bg, NULL);
1690 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1691 uzbl.behave.style_uri, NULL);
1695 cmd_resizable_txt() {
1696 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1697 uzbl.behave.resizable_txt, NULL);
1701 cmd_default_encoding() {
1702 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1703 uzbl.behave.default_encoding, NULL);
1707 cmd_enforce_96dpi() {
1708 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1709 uzbl.behave.enforce_96dpi, NULL);
1713 cmd_caret_browsing() {
1714 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1715 uzbl.behave.caret_browsing, NULL);
1719 cmd_cookie_handler() {
1720 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1721 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1722 if ((g_strcmp0(split[0], "sh") == 0) ||
1723 (g_strcmp0(split[0], "spawn") == 0)) {
1724 g_free (uzbl.behave.cookie_handler);
1725 uzbl.behave.cookie_handler =
1726 g_strdup_printf("sync_%s %s", split[0], split[1]);
1733 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1734 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1735 if ((g_strcmp0(split[0], "sh") == 0) ||
1736 (g_strcmp0(split[0], "spawn") == 0)) {
1737 g_free (uzbl.behave.new_window);
1738 uzbl.behave.new_window =
1739 g_strdup_printf("%s %s", split[0], split[1]);
1746 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1751 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1756 if(uzbl.behave.inject_html) {
1757 webkit_web_view_load_html_string (uzbl.gui.web_view,
1758 uzbl.behave.inject_html, NULL);
1767 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1768 uzbl.behave.modmask = 0;
1770 if(uzbl.behave.modkey)
1771 g_free(uzbl.behave.modkey);
1772 uzbl.behave.modkey = buf;
1774 for (i = 0; modkeys[i].key != NULL; i++) {
1775 if (g_strrstr(buf, modkeys[i].key))
1776 uzbl.behave.modmask |= modkeys[i].mask;
1782 if (*uzbl.net.useragent == ' ') {
1783 g_free (uzbl.net.useragent);
1784 uzbl.net.useragent = NULL;
1786 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1787 uzbl.net.useragent, NULL);
1793 gtk_widget_ref(uzbl.gui.scrolled_win);
1794 gtk_widget_ref(uzbl.gui.mainbar);
1795 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1796 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1798 if(uzbl.behave.status_top) {
1799 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1800 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1803 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1804 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1806 gtk_widget_unref(uzbl.gui.scrolled_win);
1807 gtk_widget_unref(uzbl.gui.mainbar);
1808 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1813 set_var_value(gchar *name, gchar *val) {
1814 uzbl_cmdprop *c = NULL;
1818 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1819 if(!c->writeable) return FALSE;
1821 /* check for the variable type */
1822 if (c->type == TYPE_STR) {
1823 buf = expand(val, 0);
1826 } else if(c->type == TYPE_INT) {
1827 int *ip = (int *)c->ptr;
1828 buf = expand(val, 0);
1829 *ip = (int)strtoul(buf, &endp, 10);
1831 } else if (c->type == TYPE_FLOAT) {
1832 float *fp = (float *)c->ptr;
1833 buf = expand(val, 0);
1834 *fp = strtod(buf, &endp);
1838 /* invoke a command specific function */
1839 if(c->func) c->func();
1846 Behaviour *b = &uzbl.behave;
1848 if(b->html_buffer->str) {
1849 webkit_web_view_load_html_string (uzbl.gui.web_view,
1850 b->html_buffer->str, b->base_url);
1851 g_string_free(b->html_buffer, TRUE);
1852 b->html_buffer = g_string_new("");
1856 enum {M_CMD, M_HTML};
1858 parse_cmd_line(const char *ctl_line, GString *result) {
1859 Behaviour *b = &uzbl.behave;
1862 if(b->mode == M_HTML) {
1863 len = strlen(b->html_endmarker);
1864 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1865 if(len == strlen(ctl_line)-1 &&
1866 !strncmp(b->html_endmarker, ctl_line, len)) {
1868 set_var_value("mode", "0");
1873 set_timeout(b->html_timeout);
1874 g_string_append(b->html_buffer, ctl_line);
1877 else if((ctl_line[0] == '#') /* Comments */
1878 || (ctl_line[0] == ' ')
1879 || (ctl_line[0] == '\n'))
1880 ; /* ignore these lines */
1881 else { /* parse a command */
1883 gchar **tokens = NULL;
1884 len = strlen(ctl_line);
1886 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1887 ctlstrip = g_strndup(ctl_line, len - 1);
1888 else ctlstrip = g_strdup(ctl_line);
1890 tokens = g_strsplit(ctlstrip, " ", 2);
1891 parse_command(tokens[0], tokens[1], result);
1898 build_stream_name(int type, const gchar* dir) {
1899 State *s = &uzbl.state;
1903 str = g_strdup_printf
1904 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1905 } else if (type == SOCKET) {
1906 str = g_strdup_printf
1907 ("%s/uzbl_socket_%s", dir, s->instance_name);
1913 control_fifo(GIOChannel *gio, GIOCondition condition) {
1914 if (uzbl.state.verbose)
1915 printf("triggered\n");
1920 if (condition & G_IO_HUP)
1921 g_error ("Fifo: Read end of pipe died!\n");
1924 g_error ("Fifo: GIOChannel broke\n");
1926 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1927 if (ret == G_IO_STATUS_ERROR) {
1928 g_error ("Fifo: Error reading: %s\n", err->message);
1932 parse_cmd_line(ctl_line, NULL);
1939 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1940 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1941 if (unlink(uzbl.comm.fifo_path) == -1)
1942 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1943 g_free(uzbl.comm.fifo_path);
1944 uzbl.comm.fifo_path = NULL;
1947 GIOChannel *chan = NULL;
1948 GError *error = NULL;
1949 gchar *path = build_stream_name(FIFO, dir);
1951 if (!file_exists(path)) {
1952 if (mkfifo (path, 0666) == 0) {
1953 // 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.
1954 chan = g_io_channel_new_file(path, "r+", &error);
1956 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1957 if (uzbl.state.verbose)
1958 printf ("init_fifo: created successfully as %s\n", path);
1959 uzbl.comm.fifo_path = path;
1961 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1962 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1963 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1964 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1966 /* if we got this far, there was an error; cleanup */
1967 if (error) g_error_free (error);
1974 control_stdin(GIOChannel *gio, GIOCondition condition) {
1976 gchar *ctl_line = NULL;
1979 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1980 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1983 parse_cmd_line(ctl_line, NULL);
1991 GIOChannel *chan = NULL;
1992 GError *error = NULL;
1994 chan = g_io_channel_unix_new(fileno(stdin));
1996 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1997 g_error ("Stdin: could not add watch\n");
1999 if (uzbl.state.verbose)
2000 printf ("Stdin: watch added successfully\n");
2003 g_error ("Stdin: Error while opening: %s\n", error->message);
2005 if (error) g_error_free (error);
2009 control_socket(GIOChannel *chan) {
2010 struct sockaddr_un remote;
2011 unsigned int t = sizeof(remote);
2013 GIOChannel *clientchan;
2015 clientsock = accept (g_io_channel_unix_get_fd(chan),
2016 (struct sockaddr *) &remote, &t);
2018 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2019 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2020 (GIOFunc) control_client_socket, clientchan);
2027 control_client_socket(GIOChannel *clientchan) {
2029 GString *result = g_string_new("");
2030 GError *error = NULL;
2034 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2035 if (ret == G_IO_STATUS_ERROR) {
2036 g_warning ("Error reading: %s\n", error->message);
2037 g_io_channel_shutdown(clientchan, TRUE, &error);
2039 } else if (ret == G_IO_STATUS_EOF) {
2040 /* shutdown and remove channel watch from main loop */
2041 g_io_channel_shutdown(clientchan, TRUE, &error);
2046 parse_cmd_line (ctl_line, result);
2047 g_string_append_c(result, '\n');
2048 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2050 if (ret == G_IO_STATUS_ERROR) {
2051 g_warning ("Error writing: %s", error->message);
2053 g_io_channel_flush(clientchan, &error);
2056 if (error) g_error_free (error);
2057 g_string_free(result, TRUE);
2063 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2064 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2065 if (unlink(uzbl.comm.socket_path) == -1)
2066 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2067 g_free(uzbl.comm.socket_path);
2068 uzbl.comm.socket_path = NULL;
2076 GIOChannel *chan = NULL;
2078 struct sockaddr_un local;
2079 gchar *path = build_stream_name(SOCKET, dir);
2081 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2083 local.sun_family = AF_UNIX;
2084 strcpy (local.sun_path, path);
2085 unlink (local.sun_path);
2087 len = strlen (local.sun_path) + sizeof (local.sun_family);
2088 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2089 if (uzbl.state.verbose)
2090 printf ("init_socket: opened in %s\n", path);
2093 if( (chan = g_io_channel_unix_new(sock)) ) {
2094 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2095 uzbl.comm.socket_path = path;
2098 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2100 /* if we got this far, there was an error; cleanup */
2107 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2108 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2110 // this function may be called very early when the templates are not set (yet), hence the checks
2112 update_title (void) {
2113 Behaviour *b = &uzbl.behave;
2116 if (b->show_status) {
2117 if (b->title_format_short) {
2118 parsed = expand(b->title_format_short, 0);
2119 if (uzbl.gui.main_window)
2120 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2123 if (b->status_format) {
2124 parsed = expand(b->status_format, 0);
2125 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2128 if (b->status_background) {
2130 gdk_color_parse (b->status_background, &color);
2131 //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)
2132 if (uzbl.gui.main_window)
2133 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2134 else if (uzbl.gui.plug)
2135 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2138 if (b->title_format_long) {
2139 parsed = expand(b->title_format_long, 0);
2140 if (uzbl.gui.main_window)
2141 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2148 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2152 retreive_geometry();
2157 key_press_cb (GtkWidget* window, GdkEventKey* event)
2159 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2163 if (event->type != GDK_KEY_PRESS ||
2164 event->keyval == GDK_Page_Up ||
2165 event->keyval == GDK_Page_Down ||
2166 event->keyval == GDK_Up ||
2167 event->keyval == GDK_Down ||
2168 event->keyval == GDK_Left ||
2169 event->keyval == GDK_Right ||
2170 event->keyval == GDK_Shift_L ||
2171 event->keyval == GDK_Shift_R)
2174 /* turn off insert mode (if always_insert_mode is not used) */
2175 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2176 set_insert_mode(uzbl.behave.always_insert_mode);
2181 if (uzbl.behave.insert_mode &&
2182 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2183 (!uzbl.behave.modmask)
2188 if (event->keyval == GDK_Escape) {
2191 dehilight(uzbl.gui.web_view, NULL, NULL);
2195 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2196 if (event->keyval == GDK_Insert) {
2198 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2199 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2201 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2204 GString* keycmd = g_string_new(uzbl.state.keycmd);
2205 g_string_append (keycmd, str);
2206 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2213 if (event->keyval == GDK_BackSpace)
2214 keycmd_bs(NULL, NULL, NULL);
2216 gboolean key_ret = FALSE;
2217 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2220 GString* keycmd = g_string_new(uzbl.state.keycmd);
2221 g_string_append(keycmd, event->string);
2222 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2225 run_keycmd(key_ret);
2227 if (key_ret) return (!uzbl.behave.insert_mode);
2232 run_keycmd(const gboolean key_ret) {
2233 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2235 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2237 parse_command(act->name, act->param, NULL);
2241 /* try if it's an incremental keycmd or one that takes args, and run it */
2242 GString* short_keys = g_string_new ("");
2243 GString* short_keys_inc = g_string_new ("");
2245 guint len = strlen(uzbl.state.keycmd);
2246 for (i=0; i<len; i++) {
2247 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2248 g_string_assign(short_keys_inc, short_keys->str);
2249 g_string_append_c(short_keys, '_');
2250 g_string_append_c(short_keys_inc, '*');
2252 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2253 /* run normal cmds only if return was pressed */
2254 exec_paramcmd(act, i);
2257 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2258 if (key_ret) /* just quit the incremental command on return */
2260 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2264 g_string_truncate(short_keys, short_keys->len - 1);
2266 g_string_free (short_keys, TRUE);
2267 g_string_free (short_keys_inc, TRUE);
2271 exec_paramcmd(const Action *act, const guint i) {
2272 GString *parampart = g_string_new (uzbl.state.keycmd);
2273 GString *actionname = g_string_new ("");
2274 GString *actionparam = g_string_new ("");
2275 g_string_erase (parampart, 0, i+1);
2277 g_string_printf (actionname, act->name, parampart->str);
2279 g_string_printf (actionparam, act->param, parampart->str);
2280 parse_command(actionname->str, actionparam->str, NULL);
2281 g_string_free(actionname, TRUE);
2282 g_string_free(actionparam, TRUE);
2283 g_string_free(parampart, TRUE);
2291 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2293 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2294 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2295 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2296 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2297 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2298 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2299 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2300 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2301 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2302 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2303 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2310 g->mainbar = gtk_hbox_new (FALSE, 0);
2312 /* keep a reference to the bar so we can re-pack it at runtime*/
2313 //sbar_ref = g_object_ref(g->mainbar);
2315 g->mainbar_label = gtk_label_new ("");
2316 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2317 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2318 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2319 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2320 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2321 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2327 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2328 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2329 gtk_widget_set_name (window, "Uzbl browser");
2330 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2331 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2332 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2339 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2340 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2341 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2348 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2350 If actname is one that calls an external command, this function will inject
2351 newargs in front of the user-provided args in that command line. They will
2352 come become after the body of the script (in sh) or after the name of
2353 the command to execute (in spawn).
2354 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2355 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2357 The return value consist of two strings: the action (sh, ...) and its args.
2359 If act is not one that calls an external command, then the given action merely
2362 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2363 /* Arrr! Here be memory leaks */
2364 gchar *actdup = g_strdup(actname);
2365 g_array_append_val(rets, actdup);
2367 if ((g_strcmp0(actname, "spawn") == 0) ||
2368 (g_strcmp0(actname, "sh") == 0) ||
2369 (g_strcmp0(actname, "sync_spawn") == 0) ||
2370 (g_strcmp0(actname, "sync_sh") == 0) ||
2371 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2373 GString *a = g_string_new("");
2374 gchar **spawnparts = split_quoted(origargs, FALSE);
2375 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2376 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2378 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2379 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2381 g_array_append_val(rets, a->str);
2382 g_string_free(a, FALSE);
2383 g_strfreev(spawnparts);
2385 gchar *origdup = g_strdup(origargs);
2386 g_array_append_val(rets, origdup);
2388 return (gchar**)g_array_free(rets, FALSE);
2392 run_handler (const gchar *act, const gchar *args) {
2393 /* Consider this code a temporary hack to make the handlers usable.
2394 In practice, all this splicing, injection, and reconstruction is
2395 inefficient, annoying and hard to manage. Potential pitfalls arise
2396 when the handler specific args 1) are not quoted (the handler
2397 callbacks should take care of this) 2) are quoted but interfere
2398 with the users' own quotation. A more ideal solution is
2399 to refactor parse_command so that it doesn't just take a string
2400 and execute it; rather than that, we should have a function which
2401 returns the argument vector parsed from the string. This vector
2402 could be modified (e.g. insert additional args into it) before
2403 passing it to the next function that actually executes it. Though
2404 it still isn't perfect for chain actions.. will reconsider & re-
2405 factor when I have the time. -duc */
2407 char **parts = g_strsplit(act, " ", 2);
2409 if (g_strcmp0(parts[0], "chain") == 0) {
2410 GString *newargs = g_string_new("");
2411 gchar **chainparts = split_quoted(parts[1], FALSE);
2413 /* for every argument in the chain, inject the handler args
2414 and make sure the new parts are wrapped in quotes */
2415 gchar **cp = chainparts;
2417 gchar *quotless = NULL;
2418 gchar **spliced_quotless = NULL; // sigh -_-;
2419 gchar **inpart = NULL;
2422 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2424 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2425 } else quotless = g_strdup(*cp);
2427 spliced_quotless = g_strsplit(quotless, " ", 2);
2428 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2429 g_strfreev(spliced_quotless);
2431 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2437 parse_command(parts[0], &(newargs->str[1]), NULL);
2438 g_string_free(newargs, TRUE);
2439 g_strfreev(chainparts);
2442 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2443 parse_command(inparts[0], inparts[1], NULL);
2451 add_binding (const gchar *key, const gchar *act) {
2452 char **parts = g_strsplit(act, " ", 2);
2459 if (uzbl.state.verbose)
2460 printf ("Binding %-10s : %s\n", key, act);
2461 action = new_action(parts[0], parts[1]);
2463 if (g_hash_table_remove (uzbl.bindings, key))
2464 g_warning ("Overwriting existing binding for \"%s\"", key);
2465 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2470 get_xdg_var (XDG_Var xdg) {
2471 const gchar* actual_value = getenv (xdg.environmental);
2472 const gchar* home = getenv ("HOME");
2473 gchar* return_value;
2475 if (! actual_value || strcmp (actual_value, "") == 0) {
2476 if (xdg.default_value) {
2477 return_value = str_replace ("~", home, xdg.default_value);
2479 return_value = NULL;
2482 return_value = str_replace("~", home, actual_value);
2485 return return_value;
2489 find_xdg_file (int xdg_type, char* filename) {
2490 /* xdg_type = 0 => config
2491 xdg_type = 1 => data
2492 xdg_type = 2 => cache*/
2494 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2495 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2498 gchar* temporary_string;
2502 if (! file_exists (temporary_file) && xdg_type != 2) {
2503 buf = get_xdg_var (XDG[3 + xdg_type]);
2504 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2507 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2508 g_free (temporary_file);
2509 temporary_file = g_strconcat (temporary_string, filename, NULL);
2513 //g_free (temporary_string); - segfaults.
2515 if (file_exists (temporary_file)) {
2516 return temporary_file;
2523 State *s = &uzbl.state;
2524 Network *n = &uzbl.net;
2526 for (i = 0; default_config[i].command != NULL; i++) {
2527 parse_cmd_line(default_config[i].command, NULL);
2530 if (g_strcmp0(s->config_file, "-") == 0) {
2531 s->config_file = NULL;
2535 else if (!s->config_file) {
2536 s->config_file = find_xdg_file (0, "/uzbl/config");
2539 if (s->config_file) {
2540 GArray* lines = read_file_by_line (s->config_file);
2544 while ((line = g_array_index(lines, gchar*, i))) {
2545 parse_cmd_line (line, NULL);
2549 g_array_free (lines, TRUE);
2551 if (uzbl.state.verbose)
2552 printf ("No configuration file loaded.\n");
2555 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2558 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2561 if (!uzbl.behave.cookie_handler)
2564 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2565 GString *s = g_string_new ("");
2566 SoupURI * soup_uri = soup_message_get_uri(msg);
2567 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2568 run_handler(uzbl.behave.cookie_handler, s->str);
2570 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2571 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2572 if ( p != NULL ) *p = '\0';
2573 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2575 if (uzbl.comm.sync_stdout)
2576 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2578 g_string_free(s, TRUE);
2582 save_cookies (SoupMessage *msg, gpointer user_data){
2586 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2587 cookie = soup_cookie_to_set_cookie_header(ck->data);
2588 SoupURI * soup_uri = soup_message_get_uri(msg);
2589 GString *s = g_string_new ("");
2590 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2591 run_handler(uzbl.behave.cookie_handler, s->str);
2593 g_string_free(s, TRUE);
2598 /* --- WEBINSPECTOR --- */
2600 hide_window_cb(GtkWidget *widget, gpointer data) {
2603 gtk_widget_hide(widget);
2607 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2610 (void) web_inspector;
2611 GtkWidget* scrolled_window;
2612 GtkWidget* new_web_view;
2615 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2616 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2617 G_CALLBACK(hide_window_cb), NULL);
2619 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2620 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2621 gtk_widget_show(g->inspector_window);
2623 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2624 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2625 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2626 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2627 gtk_widget_show(scrolled_window);
2629 new_web_view = webkit_web_view_new();
2630 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2632 return WEBKIT_WEB_VIEW(new_web_view);
2636 inspector_show_window_cb (WebKitWebInspector* inspector){
2638 gtk_widget_show(uzbl.gui.inspector_window);
2642 /* TODO: Add variables and code to make use of these functions */
2644 inspector_close_window_cb (WebKitWebInspector* inspector){
2650 inspector_attach_window_cb (WebKitWebInspector* inspector){
2656 inspector_detach_window_cb (WebKitWebInspector* inspector){
2662 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2668 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2674 set_up_inspector() {
2676 WebKitWebSettings *settings = view_settings();
2677 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2679 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2680 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2681 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2682 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2683 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2684 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2685 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2687 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2691 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2693 uzbl_cmdprop *c = v;
2698 if(c->type == TYPE_STR)
2699 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2700 else if(c->type == TYPE_INT)
2701 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2702 else if(c->type == TYPE_FLOAT)
2703 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2707 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2711 printf("bind %s = %s %s\n", (char *)k ,
2712 (char *)a->name, a->param?(char *)a->param:"");
2717 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2718 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2722 retreive_geometry() {
2724 GString *buf = g_string_new("");
2726 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2727 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2729 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2731 if(uzbl.gui.geometry)
2732 g_free(uzbl.gui.geometry);
2733 uzbl.gui.geometry = g_string_free(buf, FALSE);
2736 /* set up gtk, gobject, variable defaults and other things that tests and other
2737 * external applications need to do anyhow */
2739 initialize(int argc, char *argv[]) {
2740 if (!g_thread_supported ())
2741 g_thread_init (NULL);
2742 uzbl.state.executable_path = g_strdup(argv[0]);
2743 uzbl.state.selected_url = NULL;
2744 uzbl.state.searchtx = NULL;
2746 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2747 g_option_context_add_main_entries (context, entries, NULL);
2748 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2749 g_option_context_parse (context, &argc, &argv, NULL);
2750 g_option_context_free(context);
2752 if (uzbl.behave.print_version) {
2753 printf("Commit: %s\n", COMMIT);
2757 /* initialize hash table */
2758 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2760 uzbl.net.soup_session = webkit_get_default_session();
2761 uzbl.state.keycmd = g_strdup("");
2763 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2764 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2765 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2766 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2767 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2768 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2770 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2771 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2772 uzbl.gui.sbar.progress_w = 10;
2774 /* HTML mode defaults*/
2775 uzbl.behave.html_buffer = g_string_new("");
2776 uzbl.behave.html_endmarker = g_strdup(".");
2777 uzbl.behave.html_timeout = 60;
2778 uzbl.behave.base_url = g_strdup("http://invalid");
2780 /* default mode indicators */
2781 uzbl.behave.insert_indicator = g_strdup("I");
2782 uzbl.behave.cmd_indicator = g_strdup("C");
2784 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2785 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2786 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2787 uzbl.info.arch = ARCH;
2788 uzbl.info.commit = COMMIT;
2791 make_var_to_name_hash();
2796 #ifndef UZBL_LIBRARY
2799 main (int argc, char* argv[]) {
2800 initialize(argc, argv);
2802 gtk_init (&argc, &argv);
2804 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2805 //main_window_ref = g_object_ref(scrolled_window);
2806 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2807 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2809 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2810 GTK_WIDGET (uzbl.gui.web_view));
2812 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2816 /* initial packing */
2817 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2818 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2820 if (uzbl.state.socket_id) {
2821 uzbl.gui.plug = create_plug ();
2822 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2823 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2825 uzbl.gui.main_window = create_window ();
2826 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2827 gtk_widget_show_all (uzbl.gui.main_window);
2828 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2831 if(!uzbl.state.instance_name)
2832 uzbl.state.instance_name = itos((int)uzbl.xwin);
2834 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2836 if (uzbl.state.verbose) {
2837 printf("Uzbl start location: %s\n", argv[0]);
2838 if (uzbl.state.socket_id)
2839 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2841 printf("window_id %i\n",(int) uzbl.xwin);
2842 printf("pid %i\n", getpid ());
2843 printf("name: %s\n", uzbl.state.instance_name);
2846 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2847 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2848 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2849 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2850 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2852 if(uzbl.gui.geometry)
2855 retreive_geometry();
2857 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2858 if (argc > 1 && !uzbl.state.uri)
2859 uri_override = g_strdup(argv[1]);
2860 gboolean verbose_override = uzbl.state.verbose;
2863 set_insert_mode(FALSE);
2865 if (!uzbl.behave.show_status)
2866 gtk_widget_hide(uzbl.gui.mainbar);
2873 if (verbose_override > uzbl.state.verbose)
2874 uzbl.state.verbose = verbose_override;
2877 set_var_value("uri", uri_override);
2878 g_free(uri_override);
2879 } else if (uzbl.state.uri)
2880 cmd_load_uri(uzbl.gui.web_view, NULL);
2885 return EXIT_SUCCESS;
2889 /* vi: set et ts=4: */