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 { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
120 { "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
121 { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
122 { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
123 { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
124 { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
125 { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
126 { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
127 { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
128 { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
129 { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
130 { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
131 { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
132 { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
133 { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
134 { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
135 { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
136 { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
137 { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
138 { "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
139 { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
140 { "history_handler", PTR_V_STR(uzbl.behave.history_handler, 1, NULL)},
141 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
142 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
143 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
144 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
145 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
146 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
147 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
148 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
149 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
150 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
151 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
153 /* exported WebKitWebSettings properties */
154 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
155 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
156 { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)},
157 { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)},
158 { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)},
159 { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)},
160 { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)},
161 { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)},
162 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
163 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
164 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
165 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
166 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
167 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
168 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
169 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
170 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
171 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
172 { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
173 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
174 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
175 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
177 /* constants (not dumpable or writeable) */
178 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
179 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
180 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
181 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
182 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
183 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
184 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
185 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
186 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
187 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
188 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
190 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
195 /*@null@*/ char *key;
198 { "SHIFT", GDK_SHIFT_MASK }, // shift
199 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
200 { "CONTROL", GDK_CONTROL_MASK }, // control
201 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
202 { "MOD2", GDK_MOD2_MASK }, // 5th mod
203 { "MOD3", GDK_MOD3_MASK }, // 6th mod
204 { "MOD4", GDK_MOD4_MASK }, // 7th mod
205 { "MOD5", GDK_MOD5_MASK }, // 8th mod
206 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
207 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
208 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
209 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
210 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
211 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
212 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
213 { "META", GDK_META_MASK }, // meta (since 2.10)
218 /* construct a hash from the var_name_to_ptr array for quick access */
220 make_var_to_name_hash() {
221 const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
222 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
224 g_hash_table_insert(uzbl.comm.proto_var,
225 (gpointer) n2v_p->name,
226 (gpointer) &n2v_p->cp);
231 /* --- UTILITY FUNCTIONS --- */
232 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
234 get_exp_type(const gchar *s) {
238 else if(*(s+1) == '{')
239 return EXP_BRACED_VAR;
240 else if(*(s+1) == '<')
242 else if(*(s+1) == '[')
245 return EXP_SIMPLE_VAR;
252 * recurse == 1: don't expand '@(command)@'
253 * recurse == 2: don't expand '@<java script>@'
256 expand(const char *s, guint recurse) {
259 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
263 gchar *cmd_stdout = NULL;
265 GString *buf = g_string_new("");
266 GString *js_ret = g_string_new("");
271 g_string_append_c(buf, *++s);
276 etype = get_exp_type(s);
281 vend = strpbrk(s, end_simple_var);
282 if(!vend) vend = strchr(s, '\0');
286 vend = strchr(s, '}');
287 if(!vend) vend = strchr(s, '\0');
291 vend = strstr(s, ")@");
292 if(!vend) vend = strchr(s, '\0');
296 vend = strstr(s, ">@");
297 if(!vend) vend = strchr(s, '\0');
301 vend = strstr(s, "]@");
302 if(!vend) vend = strchr(s, '\0');
310 ret = g_strndup(s, vend-s);
312 if(etype == EXP_SIMPLE_VAR ||
313 etype == EXP_BRACED_VAR) {
314 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
315 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
316 g_string_append(buf, (gchar *)*c->ptr.s);
318 else if(c->type == TYPE_INT) {
319 g_string_append_printf(buf, "%d", *c->ptr.i);
321 else if(c->type == TYPE_FLOAT) {
322 g_string_append_printf(buf, "%f", *c->ptr.f);
326 if(etype == EXP_SIMPLE_VAR)
331 else if(recurse != 1 &&
333 mycmd = expand(ret, 1);
334 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
338 g_printerr("error on running command: %s\n", err->message);
341 else if (*cmd_stdout) {
342 size_t len = strlen(cmd_stdout);
344 if(len > 0 && cmd_stdout[len-1] == '\n')
345 cmd_stdout[--len] = '\0'; /* strip trailing newline */
347 g_string_append(buf, cmd_stdout);
352 else if(recurse != 2 &&
354 mycmd = expand(ret, 2);
355 eval_js(uzbl.gui.web_view, mycmd, js_ret);
359 g_string_append(buf, js_ret->str);
360 g_string_free(js_ret, TRUE);
361 js_ret = g_string_new("");
365 else if(etype == EXP_ESCAPE) {
366 mycmd = expand(ret, 0);
367 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
369 g_string_append(buf, escaped);
381 g_string_append_c(buf, *s);
386 g_string_free(js_ret, TRUE);
387 return g_string_free(buf, FALSE);
394 snprintf(tmp, sizeof(tmp), "%i", val);
395 return g_strdup(tmp);
399 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
402 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
405 str_replace (const char* search, const char* replace, const char* string) {
409 buf = g_strsplit (string, search, -1);
410 ret = g_strjoinv (replace, buf);
411 g_strfreev(buf); // somebody said this segfaults
417 read_file_by_line (const gchar *path) {
418 GIOChannel *chan = NULL;
419 gchar *readbuf = NULL;
421 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
424 chan = g_io_channel_new_file(path, "r", NULL);
427 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
428 const gchar* val = g_strdup (readbuf);
429 g_array_append_val (lines, val);
434 g_io_channel_unref (chan);
436 fprintf(stderr, "File '%s' not be read.\n", path);
443 parseenv (char* string) {
444 extern char** environ;
445 gchar* tmpstr = NULL;
449 while (environ[i] != NULL) {
450 gchar** env = g_strsplit (environ[i], "=", 2);
451 gchar* envname = g_strconcat ("$", env[0], NULL);
453 if (g_strrstr (string, envname) != NULL) {
454 tmpstr = g_strdup(string);
456 string = str_replace(envname, env[1], tmpstr);
461 g_strfreev (env); // somebody said this breaks uzbl
469 setup_signal(int signr, sigfunc *shandler) {
470 struct sigaction nh, oh;
472 nh.sa_handler = shandler;
473 sigemptyset(&nh.sa_mask);
476 if(sigaction(signr, &nh, &oh) < 0)
484 if (uzbl.behave.fifo_dir)
485 unlink (uzbl.comm.fifo_path);
486 if (uzbl.behave.socket_dir)
487 unlink (uzbl.comm.socket_path);
489 g_free(uzbl.state.executable_path);
490 g_free(uzbl.state.keycmd);
491 g_hash_table_destroy(uzbl.bindings);
492 g_hash_table_destroy(uzbl.behave.commands);
495 /* --- SIGNAL HANDLER --- */
498 catch_sigterm(int s) {
504 catch_sigint(int s) {
510 /* --- CALLBACKS --- */
513 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
516 (void) navigation_action;
517 (void) policy_decision;
519 const gchar* uri = webkit_network_request_get_uri (request);
520 if (uzbl.state.verbose)
521 printf("New window requested -> %s \n", uri);
522 webkit_web_policy_decision_use(policy_decision);
527 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
532 /* If we can display it, let's display it... */
533 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
534 webkit_web_policy_decision_use (policy_decision);
538 /* ...everything we can't displayed is downloaded */
539 webkit_web_policy_decision_download (policy_decision);
543 /*@null@*/ WebKitWebView*
544 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
548 if (uzbl.state.selected_url != NULL) {
549 if (uzbl.state.verbose)
550 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
551 new_window_load_uri(uzbl.state.selected_url);
553 if (uzbl.state.verbose)
554 printf("New web view -> %s\n","Nothing to open, exiting");
560 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
563 if (uzbl.behave.download_handler) {
564 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
565 if (uzbl.state.verbose)
566 printf("Download -> %s\n",uri);
567 /* if urls not escaped, we may have to escape and quote uri before this call */
568 run_handler(uzbl.behave.download_handler, uri);
573 /* scroll a bar in a given direction */
575 scroll (GtkAdjustment* bar, GArray *argv) {
579 gdouble page_size = gtk_adjustment_get_page_size(bar);
580 gdouble value = gtk_adjustment_get_value(bar);
581 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
584 value += page_size * amount * 0.01;
588 max_value = gtk_adjustment_get_upper(bar) - page_size;
590 if (value > max_value)
591 value = max_value; /* don't scroll past the end of the page */
593 gtk_adjustment_set_value (bar, value);
597 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
598 (void) page; (void) argv; (void) result;
599 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
603 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
604 (void) page; (void) argv; (void) result;
605 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
606 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
610 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
611 (void) page; (void) result;
612 scroll(uzbl.gui.bar_v, argv);
616 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) result;
618 scroll(uzbl.gui.bar_h, argv);
623 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
624 if(uzbl.state.verbose)
625 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
627 /* update geometry var with the actual geometry
628 this is necessary as some WMs don't seem to honour
629 the above setting and we don't want to end up with
630 wrong geometry information
637 if (!uzbl.behave.show_status) {
638 gtk_widget_hide(uzbl.gui.mainbar);
640 gtk_widget_show(uzbl.gui.mainbar);
646 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
651 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
655 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
660 if (uzbl.behave.show_status) {
661 gtk_widget_hide(uzbl.gui.mainbar);
663 gtk_widget_show(uzbl.gui.mainbar);
665 uzbl.behave.show_status = !uzbl.behave.show_status;
670 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
674 //Set selected_url state variable
675 g_free(uzbl.state.selected_url);
676 uzbl.state.selected_url = NULL;
678 uzbl.state.selected_url = g_strdup(link);
684 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
687 const gchar *title = webkit_web_view_get_title(web_view);
688 if (uzbl.gui.main_title)
689 g_free (uzbl.gui.main_title);
690 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
695 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
698 uzbl.gui.sbar.load_progress = progress;
700 g_free(uzbl.gui.sbar.progress_bar);
701 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
707 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
711 if (uzbl.behave.load_finish_handler)
712 run_handler(uzbl.behave.load_finish_handler, "");
715 void clear_keycmd() {
716 g_free(uzbl.state.keycmd);
717 uzbl.state.keycmd = g_strdup("");
721 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
725 uzbl.gui.sbar.load_progress = 0;
726 clear_keycmd(); // don't need old commands to remain on new page?
727 if (uzbl.behave.load_start_handler)
728 run_handler(uzbl.behave.load_start_handler, "");
732 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
735 g_free (uzbl.state.uri);
736 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
737 uzbl.state.uri = g_string_free (newuri, FALSE);
738 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
739 set_insert_mode(uzbl.behave.always_insert_mode);
742 if (uzbl.behave.load_commit_handler)
743 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
747 destroy_cb (GtkWidget* widget, gpointer data) {
755 if (uzbl.behave.history_handler) {
757 struct tm * timeinfo;
760 timeinfo = localtime ( &rawtime );
761 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
762 run_handler(uzbl.behave.history_handler, date);
767 /* VIEW funcs (little webkit wrappers) */
768 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
770 VIEWFUNC(reload_bypass_cache)
771 VIEWFUNC(stop_loading)
778 /* -- command to callback/function map for things we cannot attach to any signals */
779 struct {const char *key; CommandInfo value;} cmdlist[] =
780 { /* key function no_split */
781 { "back", {view_go_back, 0} },
782 { "forward", {view_go_forward, 0} },
783 { "scroll_vert", {scroll_vert, 0} },
784 { "scroll_horz", {scroll_horz, 0} },
785 { "scroll_begin", {scroll_begin, 0} },
786 { "scroll_end", {scroll_end, 0} },
787 { "reload", {view_reload, 0}, },
788 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
789 { "stop", {view_stop_loading, 0}, },
790 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
791 { "zoom_out", {view_zoom_out, 0}, },
792 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
793 { "uri", {load_uri, TRUE} },
794 { "js", {run_js, TRUE} },
795 { "script", {run_external_js, 0} },
796 { "toggle_status", {toggle_status_cb, 0} },
797 { "spawn", {spawn, 0} },
798 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
799 { "sh", {spawn_sh, 0} },
800 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
801 { "talk_to_socket", {talk_to_socket, 0} },
802 { "exit", {close_uzbl, 0} },
803 { "search", {search_forward_text, TRUE} },
804 { "search_reverse", {search_reverse_text, TRUE} },
805 { "dehilight", {dehilight, 0} },
806 { "toggle_insert_mode", {toggle_insert_mode, 0} },
807 { "set", {set_var, TRUE} },
808 //{ "get", {get_var, TRUE} },
809 { "bind", {act_bind, TRUE} },
810 { "dump_config", {act_dump_config, 0} },
811 { "keycmd", {keycmd, TRUE} },
812 { "keycmd_nl", {keycmd_nl, TRUE} },
813 { "keycmd_bs", {keycmd_bs, 0} },
814 { "chain", {chain, 0} },
815 { "print", {print, TRUE} },
816 { "update_gui", {update_gui, TRUE} }
823 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
825 for (i = 0; i < LENGTH(cmdlist); i++)
826 g_hash_table_insert(uzbl.behave.commands, (gpointer) cmdlist[i].key, &cmdlist[i].value);
829 /* -- CORE FUNCTIONS -- */
832 free_action(gpointer act) {
833 Action *action = (Action*)act;
834 g_free(action->name);
836 g_free(action->param);
841 new_action(const gchar *name, const gchar *param) {
842 Action *action = g_new(Action, 1);
844 action->name = g_strdup(name);
846 action->param = g_strdup(param);
848 action->param = NULL;
854 file_exists (const char * filename) {
855 return (access(filename, F_OK) == 0);
859 set_var(WebKitWebView *page, GArray *argv, GString *result) {
860 (void) page; (void) result;
861 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
862 if (split[0] != NULL) {
863 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
864 set_var_value(g_strstrip(split[0]), value);
871 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
872 (void) page; (void) argv; (void) result;
878 print(WebKitWebView *page, GArray *argv, GString *result) {
879 (void) page; (void) result;
882 buf = expand(argv_idx(argv, 0), 0);
883 g_string_assign(result, buf);
888 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
889 (void) page; (void) result;
890 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
891 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
892 add_binding(g_strstrip(split[0]), value);
910 set_mode_indicator() {
911 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
912 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
917 set_mode_indicator();
922 set_insert_mode(gboolean mode) {
923 uzbl.behave.insert_mode = mode;
924 set_mode_indicator();
928 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
929 (void) page; (void) result;
931 if (argv_idx(argv, 0)) {
932 if (strcmp (argv_idx(argv, 0), "0") == 0) {
933 set_insert_mode(FALSE);
935 set_insert_mode(TRUE);
938 set_insert_mode( !uzbl.behave.insert_mode );
945 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
948 if (argv_idx(argv, 0)) {
949 GString* newuri = g_string_new (argv_idx(argv, 0));
950 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
951 run_js(web_view, argv, NULL);
954 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
955 g_string_prepend (newuri, "http://");
956 /* if we do handle cookies, ask our handler for them */
957 webkit_web_view_load_uri (web_view, newuri->str);
958 g_string_free (newuri, TRUE);
965 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
966 size_t argumentCount, const JSValueRef arguments[],
967 JSValueRef* exception) {
972 JSStringRef js_result_string;
973 GString *result = g_string_new("");
975 if (argumentCount >= 1) {
976 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
977 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
978 char ctl_line[arg_size];
979 JSStringGetUTF8CString(arg, ctl_line, arg_size);
981 parse_cmd_line(ctl_line, result);
983 JSStringRelease(arg);
985 js_result_string = JSStringCreateWithUTF8CString(result->str);
987 g_string_free(result, TRUE);
989 return JSValueMakeString(ctx, js_result_string);
992 JSStaticFunction js_static_functions[] = {
993 {"run", js_run_command, kJSPropertyAttributeNone},
998 /* This function creates the class and its definition, only once */
999 if (!uzbl.js.initialized) {
1000 /* it would be pretty cool to make this dynamic */
1001 uzbl.js.classdef = kJSClassDefinitionEmpty;
1002 uzbl.js.classdef.staticFunctions = js_static_functions;
1004 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1010 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1011 WebKitWebFrame *frame;
1012 JSGlobalContextRef context;
1013 JSObjectRef globalobject;
1014 JSStringRef var_name;
1016 JSStringRef js_script;
1017 JSValueRef js_result;
1018 JSStringRef js_result_string;
1019 size_t js_result_size;
1023 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1024 context = webkit_web_frame_get_global_context(frame);
1025 globalobject = JSContextGetGlobalObject(context);
1027 /* uzbl javascript namespace */
1028 var_name = JSStringCreateWithUTF8CString("Uzbl");
1029 JSObjectSetProperty(context, globalobject, var_name,
1030 JSObjectMake(context, uzbl.js.classref, NULL),
1031 kJSClassAttributeNone, NULL);
1033 /* evaluate the script and get return value*/
1034 js_script = JSStringCreateWithUTF8CString(script);
1035 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1036 if (js_result && !JSValueIsUndefined(context, js_result)) {
1037 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1038 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1040 if (js_result_size) {
1041 char js_result_utf8[js_result_size];
1042 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1043 g_string_assign(result, js_result_utf8);
1046 JSStringRelease(js_result_string);
1050 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1052 JSStringRelease(var_name);
1053 JSStringRelease(js_script);
1057 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1058 if (argv_idx(argv, 0))
1059 eval_js(web_view, argv_idx(argv, 0), result);
1063 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1065 if (argv_idx(argv, 0)) {
1066 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1071 while ((line = g_array_index(lines, gchar*, i))) {
1073 js = g_strdup (line);
1075 gchar* newjs = g_strconcat (js, line, NULL);
1082 if (uzbl.state.verbose)
1083 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1085 if (argv_idx (argv, 1)) {
1086 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1090 eval_js (web_view, js, result);
1092 g_array_free (lines, TRUE);
1097 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1098 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1099 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1100 webkit_web_view_unmark_text_matches (page);
1101 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1102 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1106 if (uzbl.state.searchtx) {
1107 if (uzbl.state.verbose)
1108 printf ("Searching: %s\n", uzbl.state.searchtx);
1109 webkit_web_view_set_highlight_text_matches (page, TRUE);
1110 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1115 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1117 search_text(page, argv, TRUE);
1121 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1123 search_text(page, argv, FALSE);
1127 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1128 (void) argv; (void) result;
1129 webkit_web_view_set_highlight_text_matches (page, FALSE);
1134 new_window_load_uri (const gchar * uri) {
1135 if (uzbl.behave.new_window) {
1136 GString *s = g_string_new ("");
1137 g_string_printf(s, "'%s'", uri);
1138 run_handler(uzbl.behave.new_window, s->str);
1141 GString* to_execute = g_string_new ("");
1142 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1144 for (i = 0; entries[i].long_name != NULL; i++) {
1145 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1146 gchar** str = (gchar**)entries[i].arg_data;
1148 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1152 if (uzbl.state.verbose)
1153 printf("\n%s\n", to_execute->str);
1154 g_spawn_command_line_async (to_execute->str, NULL);
1155 g_string_free (to_execute, TRUE);
1159 chain (WebKitWebView *page, GArray *argv, GString *result) {
1160 (void) page; (void) result;
1162 gchar **parts = NULL;
1164 while ((a = argv_idx(argv, i++))) {
1165 parts = g_strsplit (a, " ", 2);
1167 parse_command(parts[0], parts[1], result);
1173 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1177 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1183 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1187 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1193 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1198 int len = strlen(uzbl.state.keycmd);
1199 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1201 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1206 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1213 /* --Statusbar functions-- */
1215 build_progressbar_ascii(int percent) {
1216 int width=uzbl.gui.sbar.progress_w;
1219 GString *bar = g_string_new("");
1221 l = (double)percent*((double)width/100.);
1222 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1224 for(i=0; i<(int)l; i++)
1225 g_string_append(bar, uzbl.gui.sbar.progress_s);
1228 g_string_append(bar, uzbl.gui.sbar.progress_u);
1230 return g_string_free(bar, FALSE);
1232 /* --End Statusbar functions-- */
1235 sharg_append(GArray *a, const gchar *str) {
1236 const gchar *s = (str ? str : "");
1237 g_array_append_val(a, s);
1240 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1242 run_command (const gchar *command, const guint npre, const gchar **args,
1243 const gboolean sync, char **output_stdout) {
1244 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1247 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1248 gchar *pid = itos(getpid());
1249 gchar *xwin = itos(uzbl.xwin);
1251 sharg_append(a, command);
1252 for (i = 0; i < npre; i++) /* add n args before the default vars */
1253 sharg_append(a, args[i]);
1254 sharg_append(a, uzbl.state.config_file);
1255 sharg_append(a, pid);
1256 sharg_append(a, xwin);
1257 sharg_append(a, uzbl.comm.fifo_path);
1258 sharg_append(a, uzbl.comm.socket_path);
1259 sharg_append(a, uzbl.state.uri);
1260 sharg_append(a, uzbl.gui.main_title);
1262 for (i = npre; i < g_strv_length((gchar**)args); i++)
1263 sharg_append(a, args[i]);
1267 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1269 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1270 NULL, NULL, output_stdout, NULL, NULL, &err);
1271 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1272 NULL, NULL, NULL, &err);
1274 if (uzbl.state.verbose) {
1275 GString *s = g_string_new("spawned:");
1276 for (i = 0; i < (a->len); i++) {
1277 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1278 g_string_append_printf(s, " %s", qarg);
1281 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1282 printf("%s\n", s->str);
1283 g_string_free(s, TRUE);
1285 printf("Stdout: %s\n", *output_stdout);
1289 g_printerr("error on run_command: %s\n", err->message);
1294 g_array_free (a, TRUE);
1299 split_quoted(const gchar* src, const gboolean unquote) {
1300 /* split on unquoted space, return array of strings;
1301 remove a layer of quotes and backslashes if unquote */
1302 if (!src) return NULL;
1304 gboolean dq = FALSE;
1305 gboolean sq = FALSE;
1306 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1307 GString *s = g_string_new ("");
1311 for (p = src; *p != '\0'; p++) {
1312 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1313 else if (*p == '\\') { g_string_append_c(s, *p++);
1314 g_string_append_c(s, *p); }
1315 else if ((*p == '"') && unquote && !sq) dq = !dq;
1316 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1318 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1319 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1321 else if ((*p == ' ') && !dq && !sq) {
1322 dup = g_strdup(s->str);
1323 g_array_append_val(a, dup);
1324 g_string_truncate(s, 0);
1325 } else g_string_append_c(s, *p);
1327 dup = g_strdup(s->str);
1328 g_array_append_val(a, dup);
1329 ret = (gchar**)a->data;
1330 g_array_free (a, FALSE);
1331 g_string_free (s, TRUE);
1336 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1337 (void)web_view; (void)result;
1338 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1339 if (argv_idx(argv, 0))
1340 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1344 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1345 (void)web_view; (void)result;
1347 if (argv_idx(argv, 0))
1348 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1349 TRUE, &uzbl.comm.sync_stdout);
1353 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1354 (void)web_view; (void)result;
1355 if (!uzbl.behave.shell_cmd) {
1356 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1361 gchar *spacer = g_strdup("");
1362 g_array_insert_val(argv, 1, spacer);
1363 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1365 for (i = 1; i < g_strv_length(cmd); i++)
1366 g_array_prepend_val(argv, cmd[i]);
1368 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1374 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1375 (void)web_view; (void)result;
1376 if (!uzbl.behave.shell_cmd) {
1377 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1382 gchar *spacer = g_strdup("");
1383 g_array_insert_val(argv, 1, spacer);
1384 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1386 for (i = 1; i < g_strv_length(cmd); i++)
1387 g_array_prepend_val(argv, cmd[i]);
1389 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1390 TRUE, &uzbl.comm.sync_stdout);
1396 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1397 (void)web_view; (void)result;
1400 struct sockaddr_un sa;
1407 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1409 /* This function could be optimised by storing a hash table of socket paths
1410 and associated connected file descriptors rather than closing and
1411 re-opening for every call. Also we could launch a script if socket connect
1414 /* First element argv[0] is path to socket. Following elements are tokens to
1415 write to the socket. We write them as a single packet with each token
1416 separated by an ASCII nul (\0). */
1418 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1423 /* copy socket path, null terminate result */
1424 sockpath = g_array_index(argv, char*, 0);
1425 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1426 sa.sun_family = AF_UNIX;
1428 /* create socket file descriptor and connect it to path */
1429 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1431 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1434 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1435 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1440 /* build request vector */
1441 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1443 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1447 for(i = 1; i < argv->len; ++i) {
1448 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1449 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1453 ret = writev(fd, iov, argv->len - 1);
1456 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1461 /* wait for a response, with a 500ms timeout */
1463 pfd.events = POLLIN;
1465 ret = poll(&pfd, 1, 500);
1467 if(ret == 0) errno = ETIMEDOUT;
1468 if(errno == EINTR) continue;
1469 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1475 /* get length of response */
1476 if(ioctl(fd, FIONREAD, &len) == -1) {
1477 g_printerr("talk_to_socket: cannot find daemon response length, "
1478 "ioctl failed (%s)\n", strerror(errno));
1483 /* if there is a response, read it */
1485 uzbl.comm.sync_stdout = g_malloc(len + 1);
1486 if(!uzbl.comm.sync_stdout) {
1487 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1491 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1493 ret = read(fd, uzbl.comm.sync_stdout, len);
1495 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1508 parse_command(const char *cmd, const char *param, GString *result) {
1511 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1513 gchar **par = split_quoted(param, TRUE);
1514 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1516 if (c->no_split) { /* don't split */
1517 sharg_append(a, param);
1519 for (i = 0; i < g_strv_length(par); i++)
1520 sharg_append(a, par[i]);
1523 if (result == NULL) {
1524 GString *result_print = g_string_new("");
1526 c->function(uzbl.gui.web_view, a, result_print);
1527 if (result_print->len)
1528 printf("%*s\n", (int)result_print->len, result_print->str);
1530 g_string_free(result_print, TRUE);
1532 c->function(uzbl.gui.web_view, a, result);
1535 g_array_free (a, TRUE);
1538 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1545 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
1546 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1547 (GType) SOUP_SESSION_PROXY_URI);
1550 suri = soup_uri_new(uzbl.net.proxy_url);
1551 g_object_set(G_OBJECT(uzbl.net.soup_session),
1552 SOUP_SESSION_PROXY_URI,
1554 soup_uri_free(suri);
1561 if(file_exists(uzbl.gui.icon)) {
1562 if (uzbl.gui.main_window)
1563 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1565 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1571 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1572 g_array_append_val (a, uzbl.state.uri);
1573 load_uri(uzbl.gui.web_view, a, NULL);
1574 g_array_free (a, TRUE);
1578 cmd_always_insert_mode() {
1579 set_insert_mode(uzbl.behave.always_insert_mode);
1585 g_object_set(G_OBJECT(uzbl.net.soup_session),
1586 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1590 cmd_max_conns_host() {
1591 g_object_set(G_OBJECT(uzbl.net.soup_session),
1592 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1597 soup_session_remove_feature
1598 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1599 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1600 /*g_free(uzbl.net.soup_logger);*/
1602 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1603 soup_session_add_feature(uzbl.net.soup_session,
1604 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1609 return webkit_web_view_get_settings(uzbl.gui.web_view);
1614 WebKitWebSettings *ws = view_settings();
1615 if (uzbl.behave.font_size > 0) {
1616 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1619 if (uzbl.behave.monospace_size > 0) {
1620 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1621 uzbl.behave.monospace_size, NULL);
1623 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1624 uzbl.behave.font_size, NULL);
1629 cmd_default_font_family() {
1630 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1631 uzbl.behave.default_font_family, NULL);
1635 cmd_monospace_font_family() {
1636 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1637 uzbl.behave.monospace_font_family, NULL);
1641 cmd_sans_serif_font_family() {
1642 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1643 uzbl.behave.sans_serif_font_family, NULL);
1647 cmd_serif_font_family() {
1648 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1649 uzbl.behave.serif_font_family, NULL);
1653 cmd_cursive_font_family() {
1654 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1655 uzbl.behave.cursive_font_family, NULL);
1659 cmd_fantasy_font_family() {
1660 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1661 uzbl.behave.fantasy_font_family, NULL);
1666 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1670 cmd_disable_plugins() {
1671 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1672 !uzbl.behave.disable_plugins, NULL);
1676 cmd_disable_scripts() {
1677 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1678 !uzbl.behave.disable_scripts, NULL);
1682 cmd_minimum_font_size() {
1683 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1684 uzbl.behave.minimum_font_size, NULL);
1687 cmd_autoload_img() {
1688 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1689 uzbl.behave.autoload_img, NULL);
1694 cmd_autoshrink_img() {
1695 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1696 uzbl.behave.autoshrink_img, NULL);
1701 cmd_enable_spellcheck() {
1702 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1703 uzbl.behave.enable_spellcheck, NULL);
1707 cmd_enable_private() {
1708 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1709 uzbl.behave.enable_private, NULL);
1714 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1715 uzbl.behave.print_bg, NULL);
1720 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1721 uzbl.behave.style_uri, NULL);
1725 cmd_resizable_txt() {
1726 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1727 uzbl.behave.resizable_txt, NULL);
1731 cmd_default_encoding() {
1732 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1733 uzbl.behave.default_encoding, NULL);
1737 cmd_enforce_96dpi() {
1738 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1739 uzbl.behave.enforce_96dpi, NULL);
1743 cmd_caret_browsing() {
1744 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1745 uzbl.behave.caret_browsing, NULL);
1749 cmd_cookie_handler() {
1750 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1751 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1752 if ((g_strcmp0(split[0], "sh") == 0) ||
1753 (g_strcmp0(split[0], "spawn") == 0)) {
1754 g_free (uzbl.behave.cookie_handler);
1755 uzbl.behave.cookie_handler =
1756 g_strdup_printf("sync_%s %s", split[0], split[1]);
1763 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1764 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1765 if ((g_strcmp0(split[0], "sh") == 0) ||
1766 (g_strcmp0(split[0], "spawn") == 0)) {
1767 g_free (uzbl.behave.new_window);
1768 uzbl.behave.new_window =
1769 g_strdup_printf("%s %s", split[0], split[1]);
1776 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1781 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1786 if(uzbl.behave.inject_html) {
1787 webkit_web_view_load_html_string (uzbl.gui.web_view,
1788 uzbl.behave.inject_html, NULL);
1797 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1798 uzbl.behave.modmask = 0;
1800 if(uzbl.behave.modkey)
1801 g_free(uzbl.behave.modkey);
1802 uzbl.behave.modkey = buf;
1804 for (i = 0; modkeys[i].key != NULL; i++) {
1805 if (g_strrstr(buf, modkeys[i].key))
1806 uzbl.behave.modmask |= modkeys[i].mask;
1812 if (*uzbl.net.useragent == ' ') {
1813 g_free (uzbl.net.useragent);
1814 uzbl.net.useragent = NULL;
1816 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1817 uzbl.net.useragent, NULL);
1823 if (!uzbl.gui.scrolled_win &&
1827 gtk_widget_ref(uzbl.gui.scrolled_win);
1828 gtk_widget_ref(uzbl.gui.mainbar);
1829 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1830 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1832 if(uzbl.behave.status_top) {
1833 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1834 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1837 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.mainbar, FALSE, TRUE, 0);
1840 gtk_widget_unref(uzbl.gui.scrolled_win);
1841 gtk_widget_unref(uzbl.gui.mainbar);
1842 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1847 set_var_value(const gchar *name, gchar *val) {
1848 uzbl_cmdprop *c = NULL;
1851 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1853 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1854 if(!c->writeable) return FALSE;
1856 /* check for the variable type */
1857 if (c->type == TYPE_STR) {
1858 buf = expand(val, 0);
1861 } else if(c->type == TYPE_INT) {
1862 buf = expand(val, 0);
1863 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1865 } else if (c->type == TYPE_FLOAT) {
1866 buf = expand(val, 0);
1867 *c->ptr.f = strtod(buf, &endp);
1871 /* invoke a command specific function */
1872 if(c->func) c->func();
1874 /* check wether name violates our naming scheme */
1875 if(strpbrk(name, invalid_chars)) {
1876 if (uzbl.state.verbose)
1877 printf("Invalid variable name\n");
1882 c = malloc(sizeof(uzbl_cmdprop));
1887 buf = expand(val, 0);
1888 c->ptr.s = malloc(sizeof(char *));
1890 g_hash_table_insert(uzbl.comm.proto_var,
1891 g_strdup(name), (gpointer) c);
1896 enum {M_CMD, M_HTML};
1898 parse_cmd_line(const char *ctl_line, GString *result) {
1901 if((ctl_line[0] == '#') /* Comments */
1902 || (ctl_line[0] == ' ')
1903 || (ctl_line[0] == '\n'))
1904 ; /* ignore these lines */
1905 else { /* parse a command */
1907 gchar **tokens = NULL;
1908 len = strlen(ctl_line);
1910 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1911 ctlstrip = g_strndup(ctl_line, len - 1);
1912 else ctlstrip = g_strdup(ctl_line);
1914 tokens = g_strsplit(ctlstrip, " ", 2);
1915 parse_command(tokens[0], tokens[1], result);
1922 build_stream_name(int type, const gchar* dir) {
1923 State *s = &uzbl.state;
1927 str = g_strdup_printf
1928 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1929 } else if (type == SOCKET) {
1930 str = g_strdup_printf
1931 ("%s/uzbl_socket_%s", dir, s->instance_name);
1937 control_fifo(GIOChannel *gio, GIOCondition condition) {
1938 if (uzbl.state.verbose)
1939 printf("triggered\n");
1944 if (condition & G_IO_HUP)
1945 g_error ("Fifo: Read end of pipe died!\n");
1948 g_error ("Fifo: GIOChannel broke\n");
1950 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1951 if (ret == G_IO_STATUS_ERROR) {
1952 g_error ("Fifo: Error reading: %s\n", err->message);
1956 parse_cmd_line(ctl_line, NULL);
1963 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1964 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1965 if (unlink(uzbl.comm.fifo_path) == -1)
1966 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1967 g_free(uzbl.comm.fifo_path);
1968 uzbl.comm.fifo_path = NULL;
1971 GIOChannel *chan = NULL;
1972 GError *error = NULL;
1973 gchar *path = build_stream_name(FIFO, dir);
1975 if (!file_exists(path)) {
1976 if (mkfifo (path, 0666) == 0) {
1977 // 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.
1978 chan = g_io_channel_new_file(path, "r+", &error);
1980 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1981 if (uzbl.state.verbose)
1982 printf ("init_fifo: created successfully as %s\n", path);
1983 uzbl.comm.fifo_path = path;
1985 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1986 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1987 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1988 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1990 /* if we got this far, there was an error; cleanup */
1991 if (error) g_error_free (error);
1998 control_stdin(GIOChannel *gio, GIOCondition condition) {
2000 gchar *ctl_line = NULL;
2003 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2004 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2007 parse_cmd_line(ctl_line, NULL);
2015 GIOChannel *chan = NULL;
2016 GError *error = NULL;
2018 chan = g_io_channel_unix_new(fileno(stdin));
2020 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2021 g_error ("Stdin: could not add watch\n");
2023 if (uzbl.state.verbose)
2024 printf ("Stdin: watch added successfully\n");
2027 g_error ("Stdin: Error while opening: %s\n", error->message);
2029 if (error) g_error_free (error);
2033 control_socket(GIOChannel *chan) {
2034 struct sockaddr_un remote;
2035 unsigned int t = sizeof(remote);
2037 GIOChannel *clientchan;
2039 clientsock = accept (g_io_channel_unix_get_fd(chan),
2040 (struct sockaddr *) &remote, &t);
2042 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2043 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2044 (GIOFunc) control_client_socket, clientchan);
2051 control_client_socket(GIOChannel *clientchan) {
2053 GString *result = g_string_new("");
2054 GError *error = NULL;
2058 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2059 if (ret == G_IO_STATUS_ERROR) {
2060 g_warning ("Error reading: %s\n", error->message);
2061 g_io_channel_shutdown(clientchan, TRUE, &error);
2063 } else if (ret == G_IO_STATUS_EOF) {
2064 /* shutdown and remove channel watch from main loop */
2065 g_io_channel_shutdown(clientchan, TRUE, &error);
2070 parse_cmd_line (ctl_line, result);
2071 g_string_append_c(result, '\n');
2072 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2074 if (ret == G_IO_STATUS_ERROR) {
2075 g_warning ("Error writing: %s", error->message);
2077 g_io_channel_flush(clientchan, &error);
2080 if (error) g_error_free (error);
2081 g_string_free(result, TRUE);
2087 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2088 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2089 if (unlink(uzbl.comm.socket_path) == -1)
2090 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2091 g_free(uzbl.comm.socket_path);
2092 uzbl.comm.socket_path = NULL;
2100 GIOChannel *chan = NULL;
2102 struct sockaddr_un local;
2103 gchar *path = build_stream_name(SOCKET, dir);
2105 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2107 local.sun_family = AF_UNIX;
2108 strcpy (local.sun_path, path);
2109 unlink (local.sun_path);
2111 len = strlen (local.sun_path) + sizeof (local.sun_family);
2112 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2113 if (uzbl.state.verbose)
2114 printf ("init_socket: opened in %s\n", path);
2117 if( (chan = g_io_channel_unix_new(sock)) ) {
2118 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2119 uzbl.comm.socket_path = path;
2122 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2124 /* if we got this far, there was an error; cleanup */
2131 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2132 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2134 // this function may be called very early when the templates are not set (yet), hence the checks
2136 update_title (void) {
2137 Behaviour *b = &uzbl.behave;
2140 if (b->show_status) {
2141 if (b->title_format_short) {
2142 parsed = expand(b->title_format_short, 0);
2143 if (uzbl.gui.main_window)
2144 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2147 if (b->status_format) {
2148 parsed = expand(b->status_format, 0);
2149 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2152 if (b->status_background) {
2154 gdk_color_parse (b->status_background, &color);
2155 //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)
2156 if (uzbl.gui.main_window)
2157 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2158 else if (uzbl.gui.plug)
2159 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2162 if (b->title_format_long) {
2163 parsed = expand(b->title_format_long, 0);
2164 if (uzbl.gui.main_window)
2165 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2172 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2176 retrieve_geometry();
2181 key_press_cb (GtkWidget* window, GdkEventKey* event)
2183 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2187 if (event->type != GDK_KEY_PRESS ||
2188 event->keyval == GDK_Page_Up ||
2189 event->keyval == GDK_Page_Down ||
2190 event->keyval == GDK_Home ||
2191 event->keyval == GDK_End ||
2192 event->keyval == GDK_Up ||
2193 event->keyval == GDK_Down ||
2194 event->keyval == GDK_Left ||
2195 event->keyval == GDK_Right ||
2196 event->keyval == GDK_Shift_L ||
2197 event->keyval == GDK_Shift_R)
2200 /* turn off insert mode (if always_insert_mode is not used) */
2201 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2202 set_insert_mode(uzbl.behave.always_insert_mode);
2207 if (uzbl.behave.insert_mode &&
2208 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2209 (!uzbl.behave.modmask)
2214 if (event->keyval == GDK_Escape) {
2217 dehilight(uzbl.gui.web_view, NULL, NULL);
2221 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2222 if (event->keyval == GDK_Insert) {
2224 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2225 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2227 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2230 GString* keycmd = g_string_new(uzbl.state.keycmd);
2231 g_string_append (keycmd, str);
2232 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2239 if (event->keyval == GDK_BackSpace)
2240 keycmd_bs(NULL, NULL, NULL);
2242 gboolean key_ret = FALSE;
2243 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2246 GString* keycmd = g_string_new(uzbl.state.keycmd);
2247 g_string_append(keycmd, event->string);
2248 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2251 run_keycmd(key_ret);
2253 if (key_ret) return (!uzbl.behave.insert_mode);
2258 run_keycmd(const gboolean key_ret) {
2259 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2261 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2263 parse_command(act->name, act->param, NULL);
2267 /* try if it's an incremental keycmd or one that takes args, and run it */
2268 GString* short_keys = g_string_new ("");
2269 GString* short_keys_inc = g_string_new ("");
2271 guint len = strlen(uzbl.state.keycmd);
2272 for (i=0; i<len; i++) {
2273 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2274 g_string_assign(short_keys_inc, short_keys->str);
2275 g_string_append_c(short_keys, '_');
2276 g_string_append_c(short_keys_inc, '*');
2278 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2279 /* run normal cmds only if return was pressed */
2280 exec_paramcmd(act, i);
2283 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2284 if (key_ret) /* just quit the incremental command on return */
2286 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2290 g_string_truncate(short_keys, short_keys->len - 1);
2292 g_string_free (short_keys, TRUE);
2293 g_string_free (short_keys_inc, TRUE);
2297 exec_paramcmd(const Action *act, const guint i) {
2298 GString *parampart = g_string_new (uzbl.state.keycmd);
2299 GString *actionname = g_string_new ("");
2300 GString *actionparam = g_string_new ("");
2301 g_string_erase (parampart, 0, i+1);
2303 g_string_printf (actionname, act->name, parampart->str);
2305 g_string_printf (actionparam, act->param, parampart->str);
2306 parse_command(actionname->str, actionparam->str, NULL);
2307 g_string_free(actionname, TRUE);
2308 g_string_free(actionparam, TRUE);
2309 g_string_free(parampart, TRUE);
2317 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2319 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2320 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2321 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2322 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2323 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2324 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2325 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2326 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2327 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2328 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2329 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2336 g->mainbar = gtk_hbox_new (FALSE, 0);
2338 /* keep a reference to the bar so we can re-pack it at runtime*/
2339 //sbar_ref = g_object_ref(g->mainbar);
2341 g->mainbar_label = gtk_label_new ("");
2342 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2343 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2344 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2345 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2346 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2347 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2353 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2354 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2355 gtk_widget_set_name (window, "Uzbl browser");
2356 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2357 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2358 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2365 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2366 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2367 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2374 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2376 If actname is one that calls an external command, this function will inject
2377 newargs in front of the user-provided args in that command line. They will
2378 come become after the body of the script (in sh) or after the name of
2379 the command to execute (in spawn).
2380 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2381 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2383 The return value consist of two strings: the action (sh, ...) and its args.
2385 If act is not one that calls an external command, then the given action merely
2388 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2389 /* Arrr! Here be memory leaks */
2390 gchar *actdup = g_strdup(actname);
2391 g_array_append_val(rets, actdup);
2393 if ((g_strcmp0(actname, "spawn") == 0) ||
2394 (g_strcmp0(actname, "sh") == 0) ||
2395 (g_strcmp0(actname, "sync_spawn") == 0) ||
2396 (g_strcmp0(actname, "sync_sh") == 0) ||
2397 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2399 GString *a = g_string_new("");
2400 gchar **spawnparts = split_quoted(origargs, FALSE);
2401 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2402 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2404 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2405 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2407 g_array_append_val(rets, a->str);
2408 g_string_free(a, FALSE);
2409 g_strfreev(spawnparts);
2411 gchar *origdup = g_strdup(origargs);
2412 g_array_append_val(rets, origdup);
2414 return (gchar**)g_array_free(rets, FALSE);
2418 run_handler (const gchar *act, const gchar *args) {
2419 /* Consider this code a temporary hack to make the handlers usable.
2420 In practice, all this splicing, injection, and reconstruction is
2421 inefficient, annoying and hard to manage. Potential pitfalls arise
2422 when the handler specific args 1) are not quoted (the handler
2423 callbacks should take care of this) 2) are quoted but interfere
2424 with the users' own quotation. A more ideal solution is
2425 to refactor parse_command so that it doesn't just take a string
2426 and execute it; rather than that, we should have a function which
2427 returns the argument vector parsed from the string. This vector
2428 could be modified (e.g. insert additional args into it) before
2429 passing it to the next function that actually executes it. Though
2430 it still isn't perfect for chain actions.. will reconsider & re-
2431 factor when I have the time. -duc */
2433 char **parts = g_strsplit(act, " ", 2);
2435 if (g_strcmp0(parts[0], "chain") == 0) {
2436 GString *newargs = g_string_new("");
2437 gchar **chainparts = split_quoted(parts[1], FALSE);
2439 /* for every argument in the chain, inject the handler args
2440 and make sure the new parts are wrapped in quotes */
2441 gchar **cp = chainparts;
2443 gchar *quotless = NULL;
2444 gchar **spliced_quotless = NULL; // sigh -_-;
2445 gchar **inpart = NULL;
2448 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2450 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2451 } else quotless = g_strdup(*cp);
2453 spliced_quotless = g_strsplit(quotless, " ", 2);
2454 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2455 g_strfreev(spliced_quotless);
2457 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2463 parse_command(parts[0], &(newargs->str[1]), NULL);
2464 g_string_free(newargs, TRUE);
2465 g_strfreev(chainparts);
2468 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2469 parse_command(inparts[0], inparts[1], NULL);
2477 add_binding (const gchar *key, const gchar *act) {
2478 char **parts = g_strsplit(act, " ", 2);
2485 if (uzbl.state.verbose)
2486 printf ("Binding %-10s : %s\n", key, act);
2487 action = new_action(parts[0], parts[1]);
2489 if (g_hash_table_remove (uzbl.bindings, key))
2490 g_warning ("Overwriting existing binding for \"%s\"", key);
2491 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2496 get_xdg_var (XDG_Var xdg) {
2497 const gchar* actual_value = getenv (xdg.environmental);
2498 const gchar* home = getenv ("HOME");
2499 gchar* return_value;
2501 if (! actual_value || strcmp (actual_value, "") == 0) {
2502 if (xdg.default_value) {
2503 return_value = str_replace ("~", home, xdg.default_value);
2505 return_value = NULL;
2508 return_value = str_replace("~", home, actual_value);
2511 return return_value;
2515 find_xdg_file (int xdg_type, const char* filename) {
2516 /* xdg_type = 0 => config
2517 xdg_type = 1 => data
2518 xdg_type = 2 => cache*/
2520 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2521 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2524 gchar* temporary_string;
2528 if (! file_exists (temporary_file) && xdg_type != 2) {
2529 buf = get_xdg_var (XDG[3 + xdg_type]);
2530 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2533 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2534 g_free (temporary_file);
2535 temporary_file = g_strconcat (temporary_string, filename, NULL);
2539 //g_free (temporary_string); - segfaults.
2541 if (file_exists (temporary_file)) {
2542 return temporary_file;
2544 g_free(temporary_file);
2550 State *s = &uzbl.state;
2551 Network *n = &uzbl.net;
2553 for (i = 0; default_config[i].command != NULL; i++) {
2554 parse_cmd_line(default_config[i].command, NULL);
2557 if (g_strcmp0(s->config_file, "-") == 0) {
2558 s->config_file = NULL;
2562 else if (!s->config_file) {
2563 s->config_file = find_xdg_file (0, "/uzbl/config");
2566 if (s->config_file) {
2567 GArray* lines = read_file_by_line (s->config_file);
2571 while ((line = g_array_index(lines, gchar*, i))) {
2572 parse_cmd_line (line, NULL);
2576 g_array_free (lines, TRUE);
2578 if (uzbl.state.verbose)
2579 printf ("No configuration file loaded.\n");
2582 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2585 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2588 if (!uzbl.behave.cookie_handler)
2591 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2592 GString *s = g_string_new ("");
2593 SoupURI * soup_uri = soup_message_get_uri(msg);
2594 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2595 run_handler(uzbl.behave.cookie_handler, s->str);
2597 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2598 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2599 if ( p != NULL ) *p = '\0';
2600 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2602 if (uzbl.comm.sync_stdout)
2603 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2605 g_string_free(s, TRUE);
2609 save_cookies (SoupMessage *msg, gpointer user_data){
2613 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2614 cookie = soup_cookie_to_set_cookie_header(ck->data);
2615 SoupURI * soup_uri = soup_message_get_uri(msg);
2616 GString *s = g_string_new ("");
2617 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2618 run_handler(uzbl.behave.cookie_handler, s->str);
2620 g_string_free(s, TRUE);
2625 /* --- WEBINSPECTOR --- */
2627 hide_window_cb(GtkWidget *widget, gpointer data) {
2630 gtk_widget_hide(widget);
2634 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2637 (void) web_inspector;
2638 GtkWidget* scrolled_window;
2639 GtkWidget* new_web_view;
2642 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2643 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2644 G_CALLBACK(hide_window_cb), NULL);
2646 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2647 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2648 gtk_widget_show(g->inspector_window);
2650 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2651 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2652 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2653 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2654 gtk_widget_show(scrolled_window);
2656 new_web_view = webkit_web_view_new();
2657 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2659 return WEBKIT_WEB_VIEW(new_web_view);
2663 inspector_show_window_cb (WebKitWebInspector* inspector){
2665 gtk_widget_show(uzbl.gui.inspector_window);
2669 /* TODO: Add variables and code to make use of these functions */
2671 inspector_close_window_cb (WebKitWebInspector* inspector){
2677 inspector_attach_window_cb (WebKitWebInspector* inspector){
2683 inspector_detach_window_cb (WebKitWebInspector* inspector){
2689 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2695 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2701 set_up_inspector() {
2703 WebKitWebSettings *settings = view_settings();
2704 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2706 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2707 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2708 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2709 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2710 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2711 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2712 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2714 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2718 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2720 uzbl_cmdprop *c = v;
2725 if(c->type == TYPE_STR)
2726 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2727 else if(c->type == TYPE_INT)
2728 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2729 else if(c->type == TYPE_FLOAT)
2730 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2734 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2738 printf("bind %s = %s %s\n", (char *)k ,
2739 (char *)a->name, a->param?(char *)a->param:"");
2744 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2745 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2749 retrieve_geometry() {
2751 GString *buf = g_string_new("");
2753 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2754 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2756 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2758 if(uzbl.gui.geometry)
2759 g_free(uzbl.gui.geometry);
2760 uzbl.gui.geometry = g_string_free(buf, FALSE);
2763 /* set up gtk, gobject, variable defaults and other things that tests and other
2764 * external applications need to do anyhow */
2766 initialize(int argc, char *argv[]) {
2767 if (!g_thread_supported ())
2768 g_thread_init (NULL);
2769 uzbl.state.executable_path = g_strdup(argv[0]);
2770 uzbl.state.selected_url = NULL;
2771 uzbl.state.searchtx = NULL;
2773 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2774 g_option_context_add_main_entries (context, entries, NULL);
2775 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2776 g_option_context_parse (context, &argc, &argv, NULL);
2777 g_option_context_free(context);
2779 if (uzbl.behave.print_version) {
2780 printf("Commit: %s\n", COMMIT);
2784 /* initialize hash table */
2785 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2787 uzbl.net.soup_session = webkit_get_default_session();
2788 uzbl.state.keycmd = g_strdup("");
2790 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2791 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2792 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2793 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2795 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2796 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2797 uzbl.gui.sbar.progress_w = 10;
2799 /* default mode indicators */
2800 uzbl.behave.insert_indicator = g_strdup("I");
2801 uzbl.behave.cmd_indicator = g_strdup("C");
2803 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2804 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2805 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2806 uzbl.info.arch = ARCH;
2807 uzbl.info.commit = COMMIT;
2810 make_var_to_name_hash();
2815 #ifndef UZBL_LIBRARY
2818 main (int argc, char* argv[]) {
2819 initialize(argc, argv);
2821 gtk_init (&argc, &argv);
2823 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2824 //main_window_ref = g_object_ref(scrolled_window);
2825 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2826 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2828 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2829 GTK_WIDGET (uzbl.gui.web_view));
2831 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2835 /* initial packing */
2836 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2837 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2839 if (uzbl.state.socket_id) {
2840 uzbl.gui.plug = create_plug ();
2841 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2842 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2844 uzbl.gui.main_window = create_window ();
2845 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2846 gtk_widget_show_all (uzbl.gui.main_window);
2847 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2850 if(!uzbl.state.instance_name)
2851 uzbl.state.instance_name = itos((int)uzbl.xwin);
2853 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2855 if (uzbl.state.verbose) {
2856 printf("Uzbl start location: %s\n", argv[0]);
2857 if (uzbl.state.socket_id)
2858 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2860 printf("window_id %i\n",(int) uzbl.xwin);
2861 printf("pid %i\n", getpid ());
2862 printf("name: %s\n", uzbl.state.instance_name);
2865 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2866 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2867 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2868 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2869 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2871 /* Check uzbl is in window mode before getting/setting geometry */
2872 if (uzbl.gui.main_window) {
2873 if(uzbl.gui.geometry)
2876 retrieve_geometry();
2879 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2880 if (argc > 1 && !uzbl.state.uri)
2881 uri_override = g_strdup(argv[1]);
2882 gboolean verbose_override = uzbl.state.verbose;
2885 set_insert_mode(FALSE);
2887 if (!uzbl.behave.show_status)
2888 gtk_widget_hide(uzbl.gui.mainbar);
2895 if (verbose_override > uzbl.state.verbose)
2896 uzbl.state.verbose = verbose_override;
2899 set_var_value("uri", uri_override);
2900 g_free(uri_override);
2901 } else if (uzbl.state.uri)
2907 return EXIT_SUCCESS;
2911 /* vi: set et ts=4: */