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])
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
45 #include <webkit/webkit.h>
46 #include <libsoup/soup.h>
47 #include <JavaScriptCore/JavaScript.h>
59 #include <sys/ioctl.h>
66 /* commandline arguments (set initial values for the state variables) */
68 GOptionEntry entries[] =
70 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
71 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
72 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
73 "Whether to print all messages or just errors.", NULL },
74 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
75 "Name of the current instance (defaults to Xorg window id)", "NAME" },
76 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
77 "Path to config file or '-' for stdin", "FILE" },
78 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
79 "Socket ID", "SOCKET" },
80 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
81 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
82 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
83 "Print the version and exit", NULL },
84 { NULL, 0, 0, 0, NULL, NULL, NULL }
87 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
89 /* associate command names to their properties */
99 /*@null@*/ void (*func)(void);
102 /* abbreviations to help keep the table's width humane */
103 #define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun }
104 #define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun }
105 #define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun }
106 #define PTR_C_STR(var, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun }
107 #define PTR_C_INT(var, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun }
108 #define PTR_C_FLOAT(var, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun }
110 const struct var_name_to_ptr_t {
113 } var_name_to_ptr[] = {
114 /* variable name pointer to variable in code dump callback function */
115 /* ---------------------------------------------------------------------------------------------- */
116 { "uri", PTR_V_STR(uzbl.state.uri, 1, cmd_load_uri)},
117 { "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)},
118 { "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)},
119 { "base_url", PTR_V_STR(uzbl.behave.base_url, 1, NULL)},
120 { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
121 { "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
122 { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
123 { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
124 { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
125 { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
126 { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
127 { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
128 { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
129 { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
130 { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
131 { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
132 { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
133 { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
134 { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
135 { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
136 { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
137 { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
138 { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
139 { "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
140 { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
141 { "history_handler", PTR_V_STR(uzbl.behave.history_handler, 1, NULL)},
142 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
143 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
144 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
145 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
146 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
147 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
148 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
149 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
150 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
151 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
152 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
154 /* exported WebKitWebSettings properties */
155 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
156 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
157 { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)},
158 { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)},
159 { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)},
160 { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)},
161 { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)},
162 { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)},
163 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
164 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
165 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
166 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
167 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
168 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
169 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
170 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
171 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
172 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
173 { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
174 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
175 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
176 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
178 /* constants (not dumpable or writeable) */
179 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
180 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
181 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
182 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
183 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
184 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
185 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
186 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
187 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
188 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
189 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
191 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
196 /*@null@*/ char *key;
199 { "SHIFT", GDK_SHIFT_MASK }, // shift
200 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
201 { "CONTROL", GDK_CONTROL_MASK }, // control
202 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
203 { "MOD2", GDK_MOD2_MASK }, // 5th mod
204 { "MOD3", GDK_MOD3_MASK }, // 6th mod
205 { "MOD4", GDK_MOD4_MASK }, // 7th mod
206 { "MOD5", GDK_MOD5_MASK }, // 8th mod
207 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
208 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
209 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
210 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
211 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
212 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
213 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
214 { "META", GDK_META_MASK }, // meta (since 2.10)
219 /* construct a hash from the var_name_to_ptr array for quick access */
221 make_var_to_name_hash() {
222 const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
223 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
225 g_hash_table_insert(uzbl.comm.proto_var,
226 (gpointer) n2v_p->name,
227 (gpointer) &n2v_p->cp);
232 /* --- UTILITY FUNCTIONS --- */
233 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
235 get_exp_type(const gchar *s) {
239 else if(*(s+1) == '{')
240 return EXP_BRACED_VAR;
241 else if(*(s+1) == '<')
243 else if(*(s+1) == '[')
246 return EXP_SIMPLE_VAR;
253 * recurse == 1: don't expand '@(command)@'
254 * recurse == 2: don't expand '@<java script>@'
257 expand(const char *s, guint recurse) {
260 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
264 gchar *cmd_stdout = NULL;
266 GString *buf = g_string_new("");
267 GString *js_ret = g_string_new("");
272 g_string_append_c(buf, *++s);
277 etype = get_exp_type(s);
282 vend = strpbrk(s, end_simple_var);
283 if(!vend) vend = strchr(s, '\0');
287 vend = strchr(s, '}');
288 if(!vend) vend = strchr(s, '\0');
292 vend = strstr(s, ")@");
293 if(!vend) vend = strchr(s, '\0');
297 vend = strstr(s, ">@");
298 if(!vend) vend = strchr(s, '\0');
302 vend = strstr(s, "]@");
303 if(!vend) vend = strchr(s, '\0');
311 ret = g_strndup(s, vend-s);
313 if(etype == EXP_SIMPLE_VAR ||
314 etype == EXP_BRACED_VAR) {
315 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
316 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
317 g_string_append(buf, (gchar *)*c->ptr.s);
319 else if(c->type == TYPE_INT) {
320 g_string_append_printf(buf, "%d", *c->ptr.i);
322 else if(c->type == TYPE_FLOAT) {
323 g_string_append_printf(buf, "%f", *c->ptr.f);
327 if(etype == EXP_SIMPLE_VAR)
332 else if(recurse != 1 &&
334 mycmd = expand(ret, 1);
335 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
339 g_printerr("error on running command: %s\n", err->message);
342 else if (*cmd_stdout) {
343 size_t len = strlen(cmd_stdout);
345 if(len > 0 && cmd_stdout[len-1] == '\n')
346 cmd_stdout[--len] = '\0'; /* strip trailing newline */
348 g_string_append(buf, cmd_stdout);
353 else if(recurse != 2 &&
355 mycmd = expand(ret, 2);
356 eval_js(uzbl.gui.web_view, mycmd, js_ret);
360 g_string_append(buf, js_ret->str);
361 g_string_free(js_ret, TRUE);
362 js_ret = g_string_new("");
366 else if(etype == EXP_ESCAPE) {
367 mycmd = expand(ret, 0);
368 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
370 g_string_append(buf, escaped);
382 g_string_append_c(buf, *s);
387 g_string_free(js_ret, TRUE);
388 return g_string_free(buf, FALSE);
395 snprintf(tmp, sizeof(tmp), "%i", val);
396 return g_strdup(tmp);
400 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
403 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
406 str_replace (const char* search, const char* replace, const char* string) {
410 buf = g_strsplit (string, search, -1);
411 ret = g_strjoinv (replace, buf);
412 g_strfreev(buf); // somebody said this segfaults
418 read_file_by_line (const gchar *path) {
419 GIOChannel *chan = NULL;
420 gchar *readbuf = NULL;
422 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
425 chan = g_io_channel_new_file(path, "r", NULL);
428 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
429 const gchar* val = g_strdup (readbuf);
430 g_array_append_val (lines, val);
435 g_io_channel_unref (chan);
437 fprintf(stderr, "File '%s' not be read.\n", path);
444 parseenv (char* string) {
445 extern char** environ;
446 gchar* tmpstr = NULL;
450 while (environ[i] != NULL) {
451 gchar** env = g_strsplit (environ[i], "=", 2);
452 gchar* envname = g_strconcat ("$", env[0], NULL);
454 if (g_strrstr (string, envname) != NULL) {
455 tmpstr = g_strdup(string);
457 string = str_replace(envname, env[1], tmpstr);
462 g_strfreev (env); // somebody said this breaks uzbl
470 setup_signal(int signr, sigfunc *shandler) {
471 struct sigaction nh, oh;
473 nh.sa_handler = shandler;
474 sigemptyset(&nh.sa_mask);
477 if(sigaction(signr, &nh, &oh) < 0)
485 if (uzbl.behave.fifo_dir)
486 unlink (uzbl.comm.fifo_path);
487 if (uzbl.behave.socket_dir)
488 unlink (uzbl.comm.socket_path);
490 g_free(uzbl.state.executable_path);
491 g_free(uzbl.state.keycmd);
492 g_hash_table_destroy(uzbl.bindings);
493 g_hash_table_destroy(uzbl.behave.commands);
496 /* --- SIGNAL HANDLER --- */
499 catch_sigterm(int s) {
505 catch_sigint(int s) {
511 /* --- CALLBACKS --- */
514 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
517 (void) navigation_action;
518 (void) policy_decision;
520 const gchar* uri = webkit_network_request_get_uri (request);
521 if (uzbl.state.verbose)
522 printf("New window requested -> %s \n", uri);
523 webkit_web_policy_decision_use(policy_decision);
528 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
533 /* If we can display it, let's display it... */
534 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
535 webkit_web_policy_decision_use (policy_decision);
539 /* ...everything we can't displayed is downloaded */
540 webkit_web_policy_decision_download (policy_decision);
544 /*@null@*/ WebKitWebView*
545 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
549 if (uzbl.state.selected_url != NULL) {
550 if (uzbl.state.verbose)
551 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
552 new_window_load_uri(uzbl.state.selected_url);
554 if (uzbl.state.verbose)
555 printf("New web view -> %s\n","Nothing to open, exiting");
561 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
564 if (uzbl.behave.download_handler) {
565 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
566 if (uzbl.state.verbose)
567 printf("Download -> %s\n",uri);
568 /* if urls not escaped, we may have to escape and quote uri before this call */
569 run_handler(uzbl.behave.download_handler, uri);
574 /* scroll a bar in a given direction */
576 scroll (GtkAdjustment* bar, GArray *argv) {
580 gdouble page_size = gtk_adjustment_get_page_size(bar);
581 gdouble value = gtk_adjustment_get_value(bar);
582 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
585 value += page_size * amount * 0.01;
589 max_value = gtk_adjustment_get_upper(bar) - page_size;
591 if (value > max_value)
592 value = max_value; /* don't scroll past the end of the page */
594 gtk_adjustment_set_value (bar, value);
598 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
599 (void) page; (void) argv; (void) result;
600 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
604 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
605 (void) page; (void) argv; (void) result;
606 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
607 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
611 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
612 (void) page; (void) result;
613 scroll(uzbl.gui.bar_v, argv);
617 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
618 (void) page; (void) result;
619 scroll(uzbl.gui.bar_h, argv);
624 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
625 if(uzbl.state.verbose)
626 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
628 /* update geometry var with the actual geometry
629 this is necessary as some WMs don't seem to honour
630 the above setting and we don't want to end up with
631 wrong geometry information
638 if (!uzbl.behave.show_status) {
639 gtk_widget_hide(uzbl.gui.mainbar);
641 gtk_widget_show(uzbl.gui.mainbar);
647 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
652 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
656 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
661 if (uzbl.behave.show_status) {
662 gtk_widget_hide(uzbl.gui.mainbar);
664 gtk_widget_show(uzbl.gui.mainbar);
666 uzbl.behave.show_status = !uzbl.behave.show_status;
671 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
675 //Set selected_url state variable
676 g_free(uzbl.state.selected_url);
677 uzbl.state.selected_url = NULL;
679 uzbl.state.selected_url = g_strdup(link);
685 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
688 const gchar *title = webkit_web_view_get_title(web_view);
689 if (uzbl.gui.main_title)
690 g_free (uzbl.gui.main_title);
691 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
696 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
699 uzbl.gui.sbar.load_progress = progress;
701 g_free(uzbl.gui.sbar.progress_bar);
702 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
708 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
712 if (uzbl.behave.load_finish_handler)
713 run_handler(uzbl.behave.load_finish_handler, "");
716 void clear_keycmd() {
717 g_free(uzbl.state.keycmd);
718 uzbl.state.keycmd = g_strdup("");
722 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
726 uzbl.gui.sbar.load_progress = 0;
727 clear_keycmd(); // don't need old commands to remain on new page?
728 if (uzbl.behave.load_start_handler)
729 run_handler(uzbl.behave.load_start_handler, "");
733 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
736 g_free (uzbl.state.uri);
737 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
738 uzbl.state.uri = g_string_free (newuri, FALSE);
739 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
740 set_insert_mode(uzbl.behave.always_insert_mode);
743 if (uzbl.behave.load_commit_handler)
744 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
748 destroy_cb (GtkWidget* widget, gpointer data) {
756 if (uzbl.behave.history_handler) {
758 struct tm * timeinfo;
761 timeinfo = localtime ( &rawtime );
762 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
763 run_handler(uzbl.behave.history_handler, date);
768 /* VIEW funcs (little webkit wrappers) */
769 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
771 VIEWFUNC(reload_bypass_cache)
772 VIEWFUNC(stop_loading)
779 /* -- command to callback/function map for things we cannot attach to any signals */
780 struct {const char *key; CommandInfo value;} cmdlist[] =
781 { /* key function no_split */
782 { "back", {view_go_back, 0} },
783 { "forward", {view_go_forward, 0} },
784 { "scroll_vert", {scroll_vert, 0} },
785 { "scroll_horz", {scroll_horz, 0} },
786 { "scroll_begin", {scroll_begin, 0} },
787 { "scroll_end", {scroll_end, 0} },
788 { "reload", {view_reload, 0}, },
789 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
790 { "stop", {view_stop_loading, 0}, },
791 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
792 { "zoom_out", {view_zoom_out, 0}, },
793 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
794 { "uri", {load_uri, TRUE} },
795 { "js", {run_js, TRUE} },
796 { "script", {run_external_js, 0} },
797 { "toggle_status", {toggle_status_cb, 0} },
798 { "spawn", {spawn, 0} },
799 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
800 { "sh", {spawn_sh, 0} },
801 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
802 { "talk_to_socket", {talk_to_socket, 0} },
803 { "exit", {close_uzbl, 0} },
804 { "search", {search_forward_text, TRUE} },
805 { "search_reverse", {search_reverse_text, TRUE} },
806 { "dehilight", {dehilight, 0} },
807 { "toggle_insert_mode", {toggle_insert_mode, 0} },
808 { "set", {set_var, TRUE} },
809 //{ "get", {get_var, TRUE} },
810 { "bind", {act_bind, TRUE} },
811 { "dump_config", {act_dump_config, 0} },
812 { "keycmd", {keycmd, TRUE} },
813 { "keycmd_nl", {keycmd_nl, TRUE} },
814 { "keycmd_bs", {keycmd_bs, 0} },
815 { "chain", {chain, 0} },
816 { "print", {print, TRUE} },
817 { "update_gui", {update_gui, TRUE} }
824 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
826 for (i = 0; i < LENGTH(cmdlist); i++)
827 g_hash_table_insert(uzbl.behave.commands, (gpointer) cmdlist[i].key, &cmdlist[i].value);
830 /* -- CORE FUNCTIONS -- */
833 free_action(gpointer act) {
834 Action *action = (Action*)act;
835 g_free(action->name);
837 g_free(action->param);
842 new_action(const gchar *name, const gchar *param) {
843 Action *action = g_new(Action, 1);
845 action->name = g_strdup(name);
847 action->param = g_strdup(param);
849 action->param = NULL;
855 file_exists (const char * filename) {
856 return (access(filename, F_OK) == 0);
860 set_var(WebKitWebView *page, GArray *argv, GString *result) {
861 (void) page; (void) result;
862 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
863 if (split[0] != NULL) {
864 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
865 set_var_value(g_strstrip(split[0]), value);
872 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
873 (void) page; (void) argv; (void) result;
879 print(WebKitWebView *page, GArray *argv, GString *result) {
880 (void) page; (void) result;
883 buf = expand(argv_idx(argv, 0), 0);
884 g_string_assign(result, buf);
889 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
890 (void) page; (void) result;
891 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
892 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
893 add_binding(g_strstrip(split[0]), value);
911 set_mode_indicator() {
912 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
913 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
918 set_mode_indicator();
923 set_insert_mode(gboolean mode) {
924 uzbl.behave.insert_mode = mode;
925 set_mode_indicator();
929 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
930 (void) page; (void) result;
932 if (argv_idx(argv, 0)) {
933 if (strcmp (argv_idx(argv, 0), "0") == 0) {
934 set_insert_mode(FALSE);
936 set_insert_mode(TRUE);
939 set_insert_mode( !uzbl.behave.insert_mode );
946 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
949 if (argv_idx(argv, 0)) {
950 GString* newuri = g_string_new (argv_idx(argv, 0));
951 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
952 run_js(web_view, argv, NULL);
955 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
956 g_string_prepend (newuri, "http://");
957 /* if we do handle cookies, ask our handler for them */
958 webkit_web_view_load_uri (web_view, newuri->str);
959 g_string_free (newuri, TRUE);
966 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
967 size_t argumentCount, const JSValueRef arguments[],
968 JSValueRef* exception) {
973 JSStringRef js_result_string;
974 GString *result = g_string_new("");
976 if (argumentCount >= 1) {
977 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
978 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
979 char ctl_line[arg_size];
980 JSStringGetUTF8CString(arg, ctl_line, arg_size);
982 parse_cmd_line(ctl_line, result);
984 JSStringRelease(arg);
986 js_result_string = JSStringCreateWithUTF8CString(result->str);
988 g_string_free(result, TRUE);
990 return JSValueMakeString(ctx, js_result_string);
993 JSStaticFunction js_static_functions[] = {
994 {"run", js_run_command, kJSPropertyAttributeNone},
999 /* This function creates the class and its definition, only once */
1000 if (!uzbl.js.initialized) {
1001 /* it would be pretty cool to make this dynamic */
1002 uzbl.js.classdef = kJSClassDefinitionEmpty;
1003 uzbl.js.classdef.staticFunctions = js_static_functions;
1005 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1011 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1012 WebKitWebFrame *frame;
1013 JSGlobalContextRef context;
1014 JSObjectRef globalobject;
1015 JSStringRef var_name;
1017 JSStringRef js_script;
1018 JSValueRef js_result;
1019 JSStringRef js_result_string;
1020 size_t js_result_size;
1024 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1025 context = webkit_web_frame_get_global_context(frame);
1026 globalobject = JSContextGetGlobalObject(context);
1028 /* uzbl javascript namespace */
1029 var_name = JSStringCreateWithUTF8CString("Uzbl");
1030 JSObjectSetProperty(context, globalobject, var_name,
1031 JSObjectMake(context, uzbl.js.classref, NULL),
1032 kJSClassAttributeNone, NULL);
1034 /* evaluate the script and get return value*/
1035 js_script = JSStringCreateWithUTF8CString(script);
1036 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1037 if (js_result && !JSValueIsUndefined(context, js_result)) {
1038 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1039 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1041 if (js_result_size) {
1042 char js_result_utf8[js_result_size];
1043 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1044 g_string_assign(result, js_result_utf8);
1047 JSStringRelease(js_result_string);
1051 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1053 JSStringRelease(var_name);
1054 JSStringRelease(js_script);
1058 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1059 if (argv_idx(argv, 0))
1060 eval_js(web_view, argv_idx(argv, 0), result);
1064 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1066 if (argv_idx(argv, 0)) {
1067 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1072 while ((line = g_array_index(lines, gchar*, i))) {
1074 js = g_strdup (line);
1076 gchar* newjs = g_strconcat (js, line, NULL);
1083 if (uzbl.state.verbose)
1084 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1086 if (argv_idx (argv, 1)) {
1087 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1091 eval_js (web_view, js, result);
1093 g_array_free (lines, TRUE);
1098 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1099 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1100 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1101 webkit_web_view_unmark_text_matches (page);
1102 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1103 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1107 if (uzbl.state.searchtx) {
1108 if (uzbl.state.verbose)
1109 printf ("Searching: %s\n", uzbl.state.searchtx);
1110 webkit_web_view_set_highlight_text_matches (page, TRUE);
1111 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1116 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1118 search_text(page, argv, TRUE);
1122 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1124 search_text(page, argv, FALSE);
1128 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1129 (void) argv; (void) result;
1130 webkit_web_view_set_highlight_text_matches (page, FALSE);
1135 new_window_load_uri (const gchar * uri) {
1136 if (uzbl.behave.new_window) {
1137 GString *s = g_string_new ("");
1138 g_string_printf(s, "'%s'", uri);
1139 run_handler(uzbl.behave.new_window, s->str);
1142 GString* to_execute = g_string_new ("");
1143 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1145 for (i = 0; entries[i].long_name != NULL; i++) {
1146 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1147 gchar** str = (gchar**)entries[i].arg_data;
1149 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1153 if (uzbl.state.verbose)
1154 printf("\n%s\n", to_execute->str);
1155 g_spawn_command_line_async (to_execute->str, NULL);
1156 g_string_free (to_execute, TRUE);
1160 chain (WebKitWebView *page, GArray *argv, GString *result) {
1161 (void) page; (void) result;
1163 gchar **parts = NULL;
1165 while ((a = argv_idx(argv, i++))) {
1166 parts = g_strsplit (a, " ", 2);
1168 parse_command(parts[0], parts[1], result);
1174 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1178 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1184 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1188 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1194 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1199 int len = strlen(uzbl.state.keycmd);
1200 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1202 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1207 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1214 /* --Statusbar functions-- */
1216 build_progressbar_ascii(int percent) {
1217 int width=uzbl.gui.sbar.progress_w;
1220 GString *bar = g_string_new("");
1222 l = (double)percent*((double)width/100.);
1223 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1225 for(i=0; i<(int)l; i++)
1226 g_string_append(bar, uzbl.gui.sbar.progress_s);
1229 g_string_append(bar, uzbl.gui.sbar.progress_u);
1231 return g_string_free(bar, FALSE);
1233 /* --End Statusbar functions-- */
1236 sharg_append(GArray *a, const gchar *str) {
1237 const gchar *s = (str ? str : "");
1238 g_array_append_val(a, s);
1241 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1243 run_command (const gchar *command, const guint npre, const gchar **args,
1244 const gboolean sync, char **output_stdout) {
1245 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1248 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1249 gchar *pid = itos(getpid());
1250 gchar *xwin = itos(uzbl.xwin);
1252 sharg_append(a, command);
1253 for (i = 0; i < npre; i++) /* add n args before the default vars */
1254 sharg_append(a, args[i]);
1255 sharg_append(a, uzbl.state.config_file);
1256 sharg_append(a, pid);
1257 sharg_append(a, xwin);
1258 sharg_append(a, uzbl.comm.fifo_path);
1259 sharg_append(a, uzbl.comm.socket_path);
1260 sharg_append(a, uzbl.state.uri);
1261 sharg_append(a, uzbl.gui.main_title);
1263 for (i = npre; i < g_strv_length((gchar**)args); i++)
1264 sharg_append(a, args[i]);
1268 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1270 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1271 NULL, NULL, output_stdout, NULL, NULL, &err);
1272 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1273 NULL, NULL, NULL, &err);
1275 if (uzbl.state.verbose) {
1276 GString *s = g_string_new("spawned:");
1277 for (i = 0; i < (a->len); i++) {
1278 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1279 g_string_append_printf(s, " %s", qarg);
1282 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1283 printf("%s\n", s->str);
1284 g_string_free(s, TRUE);
1286 printf("Stdout: %s\n", *output_stdout);
1290 g_printerr("error on run_command: %s\n", err->message);
1295 g_array_free (a, TRUE);
1300 split_quoted(const gchar* src, const gboolean unquote) {
1301 /* split on unquoted space, return array of strings;
1302 remove a layer of quotes and backslashes if unquote */
1303 if (!src) return NULL;
1305 gboolean dq = FALSE;
1306 gboolean sq = FALSE;
1307 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1308 GString *s = g_string_new ("");
1312 for (p = src; *p != '\0'; p++) {
1313 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1314 else if (*p == '\\') { g_string_append_c(s, *p++);
1315 g_string_append_c(s, *p); }
1316 else if ((*p == '"') && unquote && !sq) dq = !dq;
1317 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1319 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1320 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1322 else if ((*p == ' ') && !dq && !sq) {
1323 dup = g_strdup(s->str);
1324 g_array_append_val(a, dup);
1325 g_string_truncate(s, 0);
1326 } else g_string_append_c(s, *p);
1328 dup = g_strdup(s->str);
1329 g_array_append_val(a, dup);
1330 ret = (gchar**)a->data;
1331 g_array_free (a, FALSE);
1332 g_string_free (s, TRUE);
1337 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1338 (void)web_view; (void)result;
1339 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1340 if (argv_idx(argv, 0))
1341 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1345 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1346 (void)web_view; (void)result;
1348 if (argv_idx(argv, 0))
1349 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1350 TRUE, &uzbl.comm.sync_stdout);
1354 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1355 (void)web_view; (void)result;
1356 if (!uzbl.behave.shell_cmd) {
1357 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1362 gchar *spacer = g_strdup("");
1363 g_array_insert_val(argv, 1, spacer);
1364 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1366 for (i = 1; i < g_strv_length(cmd); i++)
1367 g_array_prepend_val(argv, cmd[i]);
1369 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1375 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1376 (void)web_view; (void)result;
1377 if (!uzbl.behave.shell_cmd) {
1378 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1383 gchar *spacer = g_strdup("");
1384 g_array_insert_val(argv, 1, spacer);
1385 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1387 for (i = 1; i < g_strv_length(cmd); i++)
1388 g_array_prepend_val(argv, cmd[i]);
1390 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1391 TRUE, &uzbl.comm.sync_stdout);
1397 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1398 (void)web_view; (void)result;
1401 struct sockaddr_un sa;
1408 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1410 /* This function could be optimised by storing a hash table of socket paths
1411 and associated connected file descriptors rather than closing and
1412 re-opening for every call. Also we could launch a script if socket connect
1415 /* First element argv[0] is path to socket. Following elements are tokens to
1416 write to the socket. We write them as a single packet with each token
1417 separated by an ASCII nul (\0). */
1419 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1424 /* copy socket path, null terminate result */
1425 sockpath = g_array_index(argv, char*, 0);
1426 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1427 sa.sun_family = AF_UNIX;
1429 /* create socket file descriptor and connect it to path */
1430 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1432 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1435 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1436 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1441 /* build request vector */
1442 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1444 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1448 for(i = 1; i < argv->len; ++i) {
1449 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1450 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1454 ret = writev(fd, iov, argv->len - 1);
1457 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1462 /* wait for a response, with a 500ms timeout */
1464 pfd.events = POLLIN;
1466 ret = poll(&pfd, 1, 500);
1468 if(ret == 0) errno = ETIMEDOUT;
1469 if(errno == EINTR) continue;
1470 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1476 /* get length of response */
1477 if(ioctl(fd, FIONREAD, &len) == -1) {
1478 g_printerr("talk_to_socket: cannot find daemon response length, "
1479 "ioctl failed (%s)\n", strerror(errno));
1484 /* if there is a response, read it */
1486 uzbl.comm.sync_stdout = g_malloc(len + 1);
1487 if(!uzbl.comm.sync_stdout) {
1488 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1492 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1494 ret = read(fd, uzbl.comm.sync_stdout, len);
1496 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1509 parse_command(const char *cmd, const char *param, GString *result) {
1512 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1514 gchar **par = split_quoted(param, TRUE);
1515 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1517 if (c->no_split) { /* don't split */
1518 sharg_append(a, param);
1520 for (i = 0; i < g_strv_length(par); i++)
1521 sharg_append(a, par[i]);
1524 if (result == NULL) {
1525 GString *result_print = g_string_new("");
1527 c->function(uzbl.gui.web_view, a, result_print);
1528 if (result_print->len)
1529 printf("%*s\n", (int)result_print->len, result_print->str);
1531 g_string_free(result_print, TRUE);
1533 c->function(uzbl.gui.web_view, a, result);
1536 g_array_free (a, TRUE);
1539 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1546 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
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);
1630 cmd_default_font_family() {
1631 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1632 uzbl.behave.default_font_family, NULL);
1636 cmd_monospace_font_family() {
1637 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1638 uzbl.behave.monospace_font_family, NULL);
1642 cmd_sans_serif_font_family() {
1643 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1644 uzbl.behave.sans_serif_font_family, NULL);
1648 cmd_serif_font_family() {
1649 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1650 uzbl.behave.serif_font_family, NULL);
1654 cmd_cursive_font_family() {
1655 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1656 uzbl.behave.cursive_font_family, NULL);
1660 cmd_fantasy_font_family() {
1661 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1662 uzbl.behave.fantasy_font_family, NULL);
1667 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1671 cmd_disable_plugins() {
1672 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1673 !uzbl.behave.disable_plugins, NULL);
1677 cmd_disable_scripts() {
1678 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1679 !uzbl.behave.disable_scripts, NULL);
1683 cmd_minimum_font_size() {
1684 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1685 uzbl.behave.minimum_font_size, NULL);
1688 cmd_autoload_img() {
1689 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1690 uzbl.behave.autoload_img, NULL);
1695 cmd_autoshrink_img() {
1696 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1697 uzbl.behave.autoshrink_img, NULL);
1702 cmd_enable_spellcheck() {
1703 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1704 uzbl.behave.enable_spellcheck, NULL);
1708 cmd_enable_private() {
1709 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1710 uzbl.behave.enable_private, NULL);
1715 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1716 uzbl.behave.print_bg, NULL);
1721 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1722 uzbl.behave.style_uri, NULL);
1726 cmd_resizable_txt() {
1727 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1728 uzbl.behave.resizable_txt, NULL);
1732 cmd_default_encoding() {
1733 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1734 uzbl.behave.default_encoding, NULL);
1738 cmd_enforce_96dpi() {
1739 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1740 uzbl.behave.enforce_96dpi, NULL);
1744 cmd_caret_browsing() {
1745 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1746 uzbl.behave.caret_browsing, NULL);
1750 cmd_cookie_handler() {
1751 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1752 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1753 if ((g_strcmp0(split[0], "sh") == 0) ||
1754 (g_strcmp0(split[0], "spawn") == 0)) {
1755 g_free (uzbl.behave.cookie_handler);
1756 uzbl.behave.cookie_handler =
1757 g_strdup_printf("sync_%s %s", split[0], split[1]);
1764 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1765 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1766 if ((g_strcmp0(split[0], "sh") == 0) ||
1767 (g_strcmp0(split[0], "spawn") == 0)) {
1768 g_free (uzbl.behave.new_window);
1769 uzbl.behave.new_window =
1770 g_strdup_printf("%s %s", split[0], split[1]);
1777 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1782 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1787 if(uzbl.behave.inject_html) {
1788 webkit_web_view_load_html_string (uzbl.gui.web_view,
1789 uzbl.behave.inject_html, NULL);
1798 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1799 uzbl.behave.modmask = 0;
1801 if(uzbl.behave.modkey)
1802 g_free(uzbl.behave.modkey);
1803 uzbl.behave.modkey = buf;
1805 for (i = 0; modkeys[i].key != NULL; i++) {
1806 if (g_strrstr(buf, modkeys[i].key))
1807 uzbl.behave.modmask |= modkeys[i].mask;
1813 if (*uzbl.net.useragent == ' ') {
1814 g_free (uzbl.net.useragent);
1815 uzbl.net.useragent = NULL;
1817 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1818 uzbl.net.useragent, NULL);
1824 if (!uzbl.gui.scrolled_win &&
1828 gtk_widget_ref(uzbl.gui.scrolled_win);
1829 gtk_widget_ref(uzbl.gui.mainbar);
1830 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1831 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1833 if(uzbl.behave.status_top) {
1834 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1835 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1838 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1839 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1841 gtk_widget_unref(uzbl.gui.scrolled_win);
1842 gtk_widget_unref(uzbl.gui.mainbar);
1843 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1848 set_var_value(const gchar *name, gchar *val) {
1849 uzbl_cmdprop *c = NULL;
1852 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1854 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1855 if(!c->writeable) return FALSE;
1857 /* check for the variable type */
1858 if (c->type == TYPE_STR) {
1859 buf = expand(val, 0);
1862 } else if(c->type == TYPE_INT) {
1863 buf = expand(val, 0);
1864 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1866 } else if (c->type == TYPE_FLOAT) {
1867 buf = expand(val, 0);
1868 *c->ptr.f = strtod(buf, &endp);
1872 /* invoke a command specific function */
1873 if(c->func) c->func();
1875 /* check wether name violates our naming scheme */
1876 if(strpbrk(name, invalid_chars)) {
1877 if (uzbl.state.verbose)
1878 printf("Invalid variable name\n");
1883 c = malloc(sizeof(uzbl_cmdprop));
1888 buf = expand(val, 0);
1889 c->ptr.s = malloc(sizeof(char *));
1891 g_hash_table_insert(uzbl.comm.proto_var,
1892 g_strdup(name), (gpointer) c);
1897 enum {M_CMD, M_HTML};
1899 parse_cmd_line(const char *ctl_line, GString *result) {
1902 if((ctl_line[0] == '#') /* Comments */
1903 || (ctl_line[0] == ' ')
1904 || (ctl_line[0] == '\n'))
1905 ; /* ignore these lines */
1906 else { /* parse a command */
1908 gchar **tokens = NULL;
1909 len = strlen(ctl_line);
1911 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1912 ctlstrip = g_strndup(ctl_line, len - 1);
1913 else ctlstrip = g_strdup(ctl_line);
1915 tokens = g_strsplit(ctlstrip, " ", 2);
1916 parse_command(tokens[0], tokens[1], result);
1923 build_stream_name(int type, const gchar* dir) {
1924 State *s = &uzbl.state;
1928 str = g_strdup_printf
1929 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1930 } else if (type == SOCKET) {
1931 str = g_strdup_printf
1932 ("%s/uzbl_socket_%s", dir, s->instance_name);
1938 control_fifo(GIOChannel *gio, GIOCondition condition) {
1939 if (uzbl.state.verbose)
1940 printf("triggered\n");
1945 if (condition & G_IO_HUP)
1946 g_error ("Fifo: Read end of pipe died!\n");
1949 g_error ("Fifo: GIOChannel broke\n");
1951 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1952 if (ret == G_IO_STATUS_ERROR) {
1953 g_error ("Fifo: Error reading: %s\n", err->message);
1957 parse_cmd_line(ctl_line, NULL);
1964 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1965 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1966 if (unlink(uzbl.comm.fifo_path) == -1)
1967 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1968 g_free(uzbl.comm.fifo_path);
1969 uzbl.comm.fifo_path = NULL;
1972 GIOChannel *chan = NULL;
1973 GError *error = NULL;
1974 gchar *path = build_stream_name(FIFO, dir);
1976 if (!file_exists(path)) {
1977 if (mkfifo (path, 0666) == 0) {
1978 // 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.
1979 chan = g_io_channel_new_file(path, "r+", &error);
1981 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1982 if (uzbl.state.verbose)
1983 printf ("init_fifo: created successfully as %s\n", path);
1984 uzbl.comm.fifo_path = path;
1986 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1987 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1988 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1989 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1991 /* if we got this far, there was an error; cleanup */
1992 if (error) g_error_free (error);
1999 control_stdin(GIOChannel *gio, GIOCondition condition) {
2001 gchar *ctl_line = NULL;
2004 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2005 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2008 parse_cmd_line(ctl_line, NULL);
2016 GIOChannel *chan = NULL;
2017 GError *error = NULL;
2019 chan = g_io_channel_unix_new(fileno(stdin));
2021 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2022 g_error ("Stdin: could not add watch\n");
2024 if (uzbl.state.verbose)
2025 printf ("Stdin: watch added successfully\n");
2028 g_error ("Stdin: Error while opening: %s\n", error->message);
2030 if (error) g_error_free (error);
2034 control_socket(GIOChannel *chan) {
2035 struct sockaddr_un remote;
2036 unsigned int t = sizeof(remote);
2038 GIOChannel *clientchan;
2040 clientsock = accept (g_io_channel_unix_get_fd(chan),
2041 (struct sockaddr *) &remote, &t);
2043 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2044 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2045 (GIOFunc) control_client_socket, clientchan);
2052 control_client_socket(GIOChannel *clientchan) {
2054 GString *result = g_string_new("");
2055 GError *error = NULL;
2059 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2060 if (ret == G_IO_STATUS_ERROR) {
2061 g_warning ("Error reading: %s\n", error->message);
2062 g_io_channel_shutdown(clientchan, TRUE, &error);
2064 } else if (ret == G_IO_STATUS_EOF) {
2065 /* shutdown and remove channel watch from main loop */
2066 g_io_channel_shutdown(clientchan, TRUE, &error);
2071 parse_cmd_line (ctl_line, result);
2072 g_string_append_c(result, '\n');
2073 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2075 if (ret == G_IO_STATUS_ERROR) {
2076 g_warning ("Error writing: %s", error->message);
2078 g_io_channel_flush(clientchan, &error);
2081 if (error) g_error_free (error);
2082 g_string_free(result, TRUE);
2088 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2089 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2090 if (unlink(uzbl.comm.socket_path) == -1)
2091 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2092 g_free(uzbl.comm.socket_path);
2093 uzbl.comm.socket_path = NULL;
2101 GIOChannel *chan = NULL;
2103 struct sockaddr_un local;
2104 gchar *path = build_stream_name(SOCKET, dir);
2106 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2108 local.sun_family = AF_UNIX;
2109 strcpy (local.sun_path, path);
2110 unlink (local.sun_path);
2112 len = strlen (local.sun_path) + sizeof (local.sun_family);
2113 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2114 if (uzbl.state.verbose)
2115 printf ("init_socket: opened in %s\n", path);
2118 if( (chan = g_io_channel_unix_new(sock)) ) {
2119 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2120 uzbl.comm.socket_path = path;
2123 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2125 /* if we got this far, there was an error; cleanup */
2132 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2133 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2135 // this function may be called very early when the templates are not set (yet), hence the checks
2137 update_title (void) {
2138 Behaviour *b = &uzbl.behave;
2141 if (b->show_status) {
2142 if (b->title_format_short) {
2143 parsed = expand(b->title_format_short, 0);
2144 if (uzbl.gui.main_window)
2145 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2148 if (b->status_format) {
2149 parsed = expand(b->status_format, 0);
2150 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2153 if (b->status_background) {
2155 gdk_color_parse (b->status_background, &color);
2156 //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)
2157 if (uzbl.gui.main_window)
2158 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2159 else if (uzbl.gui.plug)
2160 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2163 if (b->title_format_long) {
2164 parsed = expand(b->title_format_long, 0);
2165 if (uzbl.gui.main_window)
2166 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2173 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2177 retrieve_geometry();
2182 key_press_cb (GtkWidget* window, GdkEventKey* event)
2184 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2188 if (event->type != GDK_KEY_PRESS ||
2189 event->keyval == GDK_Page_Up ||
2190 event->keyval == GDK_Page_Down ||
2191 event->keyval == GDK_Up ||
2192 event->keyval == GDK_Down ||
2193 event->keyval == GDK_Left ||
2194 event->keyval == GDK_Right ||
2195 event->keyval == GDK_Shift_L ||
2196 event->keyval == GDK_Shift_R)
2199 /* turn off insert mode (if always_insert_mode is not used) */
2200 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2201 set_insert_mode(uzbl.behave.always_insert_mode);
2206 if (uzbl.behave.insert_mode &&
2207 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2208 (!uzbl.behave.modmask)
2213 if (event->keyval == GDK_Escape) {
2216 dehilight(uzbl.gui.web_view, NULL, NULL);
2220 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2221 if (event->keyval == GDK_Insert) {
2223 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2224 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2226 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2229 GString* keycmd = g_string_new(uzbl.state.keycmd);
2230 g_string_append (keycmd, str);
2231 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2238 if (event->keyval == GDK_BackSpace)
2239 keycmd_bs(NULL, NULL, NULL);
2241 gboolean key_ret = FALSE;
2242 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2245 GString* keycmd = g_string_new(uzbl.state.keycmd);
2246 g_string_append(keycmd, event->string);
2247 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2250 run_keycmd(key_ret);
2252 if (key_ret) return (!uzbl.behave.insert_mode);
2257 run_keycmd(const gboolean key_ret) {
2258 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2260 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2262 parse_command(act->name, act->param, NULL);
2266 /* try if it's an incremental keycmd or one that takes args, and run it */
2267 GString* short_keys = g_string_new ("");
2268 GString* short_keys_inc = g_string_new ("");
2270 guint len = strlen(uzbl.state.keycmd);
2271 for (i=0; i<len; i++) {
2272 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2273 g_string_assign(short_keys_inc, short_keys->str);
2274 g_string_append_c(short_keys, '_');
2275 g_string_append_c(short_keys_inc, '*');
2277 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2278 /* run normal cmds only if return was pressed */
2279 exec_paramcmd(act, i);
2282 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2283 if (key_ret) /* just quit the incremental command on return */
2285 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2289 g_string_truncate(short_keys, short_keys->len - 1);
2291 g_string_free (short_keys, TRUE);
2292 g_string_free (short_keys_inc, TRUE);
2296 exec_paramcmd(const Action *act, const guint i) {
2297 GString *parampart = g_string_new (uzbl.state.keycmd);
2298 GString *actionname = g_string_new ("");
2299 GString *actionparam = g_string_new ("");
2300 g_string_erase (parampart, 0, i+1);
2302 g_string_printf (actionname, act->name, parampart->str);
2304 g_string_printf (actionparam, act->param, parampart->str);
2305 parse_command(actionname->str, actionparam->str, NULL);
2306 g_string_free(actionname, TRUE);
2307 g_string_free(actionparam, TRUE);
2308 g_string_free(parampart, TRUE);
2316 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2318 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2319 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2320 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2321 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2322 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2323 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2324 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2325 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2326 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2327 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2328 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2335 g->mainbar = gtk_hbox_new (FALSE, 0);
2337 /* keep a reference to the bar so we can re-pack it at runtime*/
2338 //sbar_ref = g_object_ref(g->mainbar);
2340 g->mainbar_label = gtk_label_new ("");
2341 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2342 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2343 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2344 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2345 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2346 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2352 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2353 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2354 gtk_widget_set_name (window, "Uzbl browser");
2355 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2356 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2357 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2364 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2365 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2366 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2373 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2375 If actname is one that calls an external command, this function will inject
2376 newargs in front of the user-provided args in that command line. They will
2377 come become after the body of the script (in sh) or after the name of
2378 the command to execute (in spawn).
2379 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2380 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2382 The return value consist of two strings: the action (sh, ...) and its args.
2384 If act is not one that calls an external command, then the given action merely
2387 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2388 /* Arrr! Here be memory leaks */
2389 gchar *actdup = g_strdup(actname);
2390 g_array_append_val(rets, actdup);
2392 if ((g_strcmp0(actname, "spawn") == 0) ||
2393 (g_strcmp0(actname, "sh") == 0) ||
2394 (g_strcmp0(actname, "sync_spawn") == 0) ||
2395 (g_strcmp0(actname, "sync_sh") == 0) ||
2396 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2398 GString *a = g_string_new("");
2399 gchar **spawnparts = split_quoted(origargs, FALSE);
2400 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2401 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2403 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2404 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2406 g_array_append_val(rets, a->str);
2407 g_string_free(a, FALSE);
2408 g_strfreev(spawnparts);
2410 gchar *origdup = g_strdup(origargs);
2411 g_array_append_val(rets, origdup);
2413 return (gchar**)g_array_free(rets, FALSE);
2417 run_handler (const gchar *act, const gchar *args) {
2418 /* Consider this code a temporary hack to make the handlers usable.
2419 In practice, all this splicing, injection, and reconstruction is
2420 inefficient, annoying and hard to manage. Potential pitfalls arise
2421 when the handler specific args 1) are not quoted (the handler
2422 callbacks should take care of this) 2) are quoted but interfere
2423 with the users' own quotation. A more ideal solution is
2424 to refactor parse_command so that it doesn't just take a string
2425 and execute it; rather than that, we should have a function which
2426 returns the argument vector parsed from the string. This vector
2427 could be modified (e.g. insert additional args into it) before
2428 passing it to the next function that actually executes it. Though
2429 it still isn't perfect for chain actions.. will reconsider & re-
2430 factor when I have the time. -duc */
2432 char **parts = g_strsplit(act, " ", 2);
2434 if (g_strcmp0(parts[0], "chain") == 0) {
2435 GString *newargs = g_string_new("");
2436 gchar **chainparts = split_quoted(parts[1], FALSE);
2438 /* for every argument in the chain, inject the handler args
2439 and make sure the new parts are wrapped in quotes */
2440 gchar **cp = chainparts;
2442 gchar *quotless = NULL;
2443 gchar **spliced_quotless = NULL; // sigh -_-;
2444 gchar **inpart = NULL;
2447 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2449 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2450 } else quotless = g_strdup(*cp);
2452 spliced_quotless = g_strsplit(quotless, " ", 2);
2453 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2454 g_strfreev(spliced_quotless);
2456 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2462 parse_command(parts[0], &(newargs->str[1]), NULL);
2463 g_string_free(newargs, TRUE);
2464 g_strfreev(chainparts);
2467 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2468 parse_command(inparts[0], inparts[1], NULL);
2476 add_binding (const gchar *key, const gchar *act) {
2477 char **parts = g_strsplit(act, " ", 2);
2484 if (uzbl.state.verbose)
2485 printf ("Binding %-10s : %s\n", key, act);
2486 action = new_action(parts[0], parts[1]);
2488 if (g_hash_table_remove (uzbl.bindings, key))
2489 g_warning ("Overwriting existing binding for \"%s\"", key);
2490 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2495 get_xdg_var (XDG_Var xdg) {
2496 const gchar* actual_value = getenv (xdg.environmental);
2497 const gchar* home = getenv ("HOME");
2498 gchar* return_value;
2500 if (! actual_value || strcmp (actual_value, "") == 0) {
2501 if (xdg.default_value) {
2502 return_value = str_replace ("~", home, xdg.default_value);
2504 return_value = NULL;
2507 return_value = str_replace("~", home, actual_value);
2510 return return_value;
2514 find_xdg_file (int xdg_type, const char* filename) {
2515 /* xdg_type = 0 => config
2516 xdg_type = 1 => data
2517 xdg_type = 2 => cache*/
2519 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2520 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2523 gchar* temporary_string;
2527 if (! file_exists (temporary_file) && xdg_type != 2) {
2528 buf = get_xdg_var (XDG[3 + xdg_type]);
2529 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2532 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2533 g_free (temporary_file);
2534 temporary_file = g_strconcat (temporary_string, filename, NULL);
2538 //g_free (temporary_string); - segfaults.
2540 if (file_exists (temporary_file)) {
2541 return temporary_file;
2543 g_free(temporary_file);
2549 State *s = &uzbl.state;
2550 Network *n = &uzbl.net;
2552 for (i = 0; default_config[i].command != NULL; i++) {
2553 parse_cmd_line(default_config[i].command, NULL);
2556 if (g_strcmp0(s->config_file, "-") == 0) {
2557 s->config_file = NULL;
2561 else if (!s->config_file) {
2562 s->config_file = find_xdg_file (0, "/uzbl/config");
2565 if (s->config_file) {
2566 GArray* lines = read_file_by_line (s->config_file);
2570 while ((line = g_array_index(lines, gchar*, i))) {
2571 parse_cmd_line (line, NULL);
2575 g_array_free (lines, TRUE);
2577 if (uzbl.state.verbose)
2578 printf ("No configuration file loaded.\n");
2581 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2584 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2587 if (!uzbl.behave.cookie_handler)
2590 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2591 GString *s = g_string_new ("");
2592 SoupURI * soup_uri = soup_message_get_uri(msg);
2593 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2594 run_handler(uzbl.behave.cookie_handler, s->str);
2596 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2597 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2598 if ( p != NULL ) *p = '\0';
2599 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2601 if (uzbl.comm.sync_stdout)
2602 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2604 g_string_free(s, TRUE);
2608 save_cookies (SoupMessage *msg, gpointer user_data){
2612 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2613 cookie = soup_cookie_to_set_cookie_header(ck->data);
2614 SoupURI * soup_uri = soup_message_get_uri(msg);
2615 GString *s = g_string_new ("");
2616 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2617 run_handler(uzbl.behave.cookie_handler, s->str);
2619 g_string_free(s, TRUE);
2624 /* --- WEBINSPECTOR --- */
2626 hide_window_cb(GtkWidget *widget, gpointer data) {
2629 gtk_widget_hide(widget);
2633 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2636 (void) web_inspector;
2637 GtkWidget* scrolled_window;
2638 GtkWidget* new_web_view;
2641 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2642 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2643 G_CALLBACK(hide_window_cb), NULL);
2645 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2646 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2647 gtk_widget_show(g->inspector_window);
2649 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2650 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2651 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2652 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2653 gtk_widget_show(scrolled_window);
2655 new_web_view = webkit_web_view_new();
2656 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2658 return WEBKIT_WEB_VIEW(new_web_view);
2662 inspector_show_window_cb (WebKitWebInspector* inspector){
2664 gtk_widget_show(uzbl.gui.inspector_window);
2668 /* TODO: Add variables and code to make use of these functions */
2670 inspector_close_window_cb (WebKitWebInspector* inspector){
2676 inspector_attach_window_cb (WebKitWebInspector* inspector){
2682 inspector_detach_window_cb (WebKitWebInspector* inspector){
2688 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2694 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2700 set_up_inspector() {
2702 WebKitWebSettings *settings = view_settings();
2703 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2705 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2706 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2707 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2708 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2709 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2710 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2711 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2713 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2717 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2719 uzbl_cmdprop *c = v;
2724 if(c->type == TYPE_STR)
2725 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2726 else if(c->type == TYPE_INT)
2727 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2728 else if(c->type == TYPE_FLOAT)
2729 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2733 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2737 printf("bind %s = %s %s\n", (char *)k ,
2738 (char *)a->name, a->param?(char *)a->param:"");
2743 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2744 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2748 retrieve_geometry() {
2750 GString *buf = g_string_new("");
2752 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2753 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2755 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2757 if(uzbl.gui.geometry)
2758 g_free(uzbl.gui.geometry);
2759 uzbl.gui.geometry = g_string_free(buf, FALSE);
2762 /* set up gtk, gobject, variable defaults and other things that tests and other
2763 * external applications need to do anyhow */
2765 initialize(int argc, char *argv[]) {
2766 if (!g_thread_supported ())
2767 g_thread_init (NULL);
2768 uzbl.state.executable_path = g_strdup(argv[0]);
2769 uzbl.state.selected_url = NULL;
2770 uzbl.state.searchtx = NULL;
2772 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2773 g_option_context_add_main_entries (context, entries, NULL);
2774 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2775 g_option_context_parse (context, &argc, &argv, NULL);
2776 g_option_context_free(context);
2778 if (uzbl.behave.print_version) {
2779 printf("Commit: %s\n", COMMIT);
2783 /* initialize hash table */
2784 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2786 uzbl.net.soup_session = webkit_get_default_session();
2787 uzbl.state.keycmd = g_strdup("");
2789 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2790 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2791 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2792 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2794 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2795 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2796 uzbl.gui.sbar.progress_w = 10;
2798 /* default mode indicators */
2799 uzbl.behave.insert_indicator = g_strdup("I");
2800 uzbl.behave.cmd_indicator = g_strdup("C");
2802 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2803 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2804 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2805 uzbl.info.arch = ARCH;
2806 uzbl.info.commit = COMMIT;
2809 make_var_to_name_hash();
2814 #ifndef UZBL_LIBRARY
2817 main (int argc, char* argv[]) {
2818 initialize(argc, argv);
2820 gtk_init (&argc, &argv);
2822 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2823 //main_window_ref = g_object_ref(scrolled_window);
2824 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2825 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2827 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2828 GTK_WIDGET (uzbl.gui.web_view));
2830 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2834 /* initial packing */
2835 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2836 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2838 if (uzbl.state.socket_id) {
2839 uzbl.gui.plug = create_plug ();
2840 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2841 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2843 uzbl.gui.main_window = create_window ();
2844 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2845 gtk_widget_show_all (uzbl.gui.main_window);
2846 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2849 if(!uzbl.state.instance_name)
2850 uzbl.state.instance_name = itos((int)uzbl.xwin);
2852 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2854 if (uzbl.state.verbose) {
2855 printf("Uzbl start location: %s\n", argv[0]);
2856 if (uzbl.state.socket_id)
2857 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2859 printf("window_id %i\n",(int) uzbl.xwin);
2860 printf("pid %i\n", getpid ());
2861 printf("name: %s\n", uzbl.state.instance_name);
2864 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2865 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2866 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2867 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2868 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2870 /* Check uzbl is in window mode before getting/setting geometry */
2871 if (uzbl.gui.main_window) {
2872 if(uzbl.gui.geometry)
2875 retrieve_geometry();
2878 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2879 if (argc > 1 && !uzbl.state.uri)
2880 uri_override = g_strdup(argv[1]);
2881 gboolean verbose_override = uzbl.state.verbose;
2884 set_insert_mode(FALSE);
2886 if (!uzbl.behave.show_status)
2887 gtk_widget_hide(uzbl.gui.mainbar);
2894 if (verbose_override > uzbl.state.verbose)
2895 uzbl.state.verbose = verbose_override;
2898 set_var_value("uri", uri_override);
2899 g_free(uri_override);
2900 } else if (uzbl.state.uri)
2906 return EXIT_SUCCESS;
2910 /* vi: set et ts=4: */