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 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
141 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
142 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
143 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
144 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
145 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
146 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
147 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
148 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
149 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
150 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
152 /* exported WebKitWebSettings properties */
153 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
154 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
155 { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)},
156 { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)},
157 { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)},
158 { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)},
159 { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)},
160 { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)},
161 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
162 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
163 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
164 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
165 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
166 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
167 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
168 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
169 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
170 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
171 { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
172 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
173 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
174 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
176 /* constants (not dumpable or writeable) */
177 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
178 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
179 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
180 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
181 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
182 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
183 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
184 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
185 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
186 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
187 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
189 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
194 /*@null@*/ char *key;
197 { "SHIFT", GDK_SHIFT_MASK }, // shift
198 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
199 { "CONTROL", GDK_CONTROL_MASK }, // control
200 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
201 { "MOD2", GDK_MOD2_MASK }, // 5th mod
202 { "MOD3", GDK_MOD3_MASK }, // 6th mod
203 { "MOD4", GDK_MOD4_MASK }, // 7th mod
204 { "MOD5", GDK_MOD5_MASK }, // 8th mod
205 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
206 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
207 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
208 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
209 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
210 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
211 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
212 { "META", GDK_META_MASK }, // meta (since 2.10)
217 /* construct a hash from the var_name_to_ptr array for quick access */
219 make_var_to_name_hash() {
220 const struct var_name_to_ptr_t *n2v_p = var_name_to_ptr;
221 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
223 g_hash_table_insert(uzbl.comm.proto_var,
224 (gpointer) n2v_p->name,
225 (gpointer) &n2v_p->cp);
230 /* --- UTILITY FUNCTIONS --- */
231 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
233 get_exp_type(const gchar *s) {
237 else if(*(s+1) == '{')
238 return EXP_BRACED_VAR;
239 else if(*(s+1) == '<')
241 else if(*(s+1) == '[')
244 return EXP_SIMPLE_VAR;
251 * recurse == 1: don't expand '@(command)@'
252 * recurse == 2: don't expand '@<java script>@'
255 expand(const char *s, guint recurse) {
258 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
262 gchar *cmd_stdout = NULL;
264 GString *buf = g_string_new("");
265 GString *js_ret = g_string_new("");
270 g_string_append_c(buf, *++s);
275 etype = get_exp_type(s);
280 vend = strpbrk(s, end_simple_var);
281 if(!vend) vend = strchr(s, '\0');
285 vend = strchr(s, '}');
286 if(!vend) vend = strchr(s, '\0');
290 vend = strstr(s, ")@");
291 if(!vend) vend = strchr(s, '\0');
295 vend = strstr(s, ">@");
296 if(!vend) vend = strchr(s, '\0');
300 vend = strstr(s, "]@");
301 if(!vend) vend = strchr(s, '\0');
309 ret = g_strndup(s, vend-s);
311 if(etype == EXP_SIMPLE_VAR ||
312 etype == EXP_BRACED_VAR) {
313 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
314 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
315 g_string_append(buf, (gchar *)*c->ptr.s);
317 else if(c->type == TYPE_INT) {
318 g_string_append_printf(buf, "%d", *c->ptr.i);
320 else if(c->type == TYPE_FLOAT) {
321 g_string_append_printf(buf, "%f", *c->ptr.f);
325 if(etype == EXP_SIMPLE_VAR)
330 else if(recurse != 1 &&
332 mycmd = expand(ret, 1);
333 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
337 g_printerr("error on running command: %s\n", err->message);
340 else if (*cmd_stdout) {
341 size_t len = strlen(cmd_stdout);
343 if(len > 0 && cmd_stdout[len-1] == '\n')
344 cmd_stdout[--len] = '\0'; /* strip trailing newline */
346 g_string_append(buf, cmd_stdout);
351 else if(recurse != 2 &&
353 mycmd = expand(ret, 2);
354 eval_js(uzbl.gui.web_view, mycmd, js_ret);
358 g_string_append(buf, js_ret->str);
359 g_string_free(js_ret, TRUE);
360 js_ret = g_string_new("");
364 else if(etype == EXP_ESCAPE) {
365 mycmd = expand(ret, 0);
366 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
368 g_string_append(buf, escaped);
380 g_string_append_c(buf, *s);
385 g_string_free(js_ret, TRUE);
386 return g_string_free(buf, FALSE);
393 snprintf(tmp, sizeof(tmp), "%i", val);
394 return g_strdup(tmp);
398 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
401 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
404 str_replace (const char* search, const char* replace, const char* string) {
408 buf = g_strsplit (string, search, -1);
409 ret = g_strjoinv (replace, buf);
410 g_strfreev(buf); // somebody said this segfaults
416 read_file_by_line (const gchar *path) {
417 GIOChannel *chan = NULL;
418 gchar *readbuf = NULL;
420 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
423 chan = g_io_channel_new_file(path, "r", NULL);
426 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
427 const gchar* val = g_strdup (readbuf);
428 g_array_append_val (lines, val);
433 g_io_channel_unref (chan);
435 fprintf(stderr, "File '%s' not be read.\n", path);
442 parseenv (char* string) {
443 extern char** environ;
444 gchar* tmpstr = NULL;
448 while (environ[i] != NULL) {
449 gchar** env = g_strsplit (environ[i], "=", 2);
450 gchar* envname = g_strconcat ("$", env[0], NULL);
452 if (g_strrstr (string, envname) != NULL) {
453 tmpstr = g_strdup(string);
455 string = str_replace(envname, env[1], tmpstr);
460 g_strfreev (env); // somebody said this breaks uzbl
468 setup_signal(int signr, sigfunc *shandler) {
469 struct sigaction nh, oh;
471 nh.sa_handler = shandler;
472 sigemptyset(&nh.sa_mask);
475 if(sigaction(signr, &nh, &oh) < 0)
483 if (uzbl.behave.fifo_dir)
484 unlink (uzbl.comm.fifo_path);
485 if (uzbl.behave.socket_dir)
486 unlink (uzbl.comm.socket_path);
488 g_free(uzbl.state.executable_path);
489 g_free(uzbl.state.keycmd);
490 g_hash_table_destroy(uzbl.bindings);
491 g_hash_table_destroy(uzbl.behave.commands);
494 /* --- SIGNAL HANDLER --- */
497 catch_sigterm(int s) {
503 catch_sigint(int s) {
509 /* --- CALLBACKS --- */
512 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
515 (void) navigation_action;
516 (void) policy_decision;
518 const gchar* uri = webkit_network_request_get_uri (request);
519 if (uzbl.state.verbose)
520 printf("New window requested -> %s \n", uri);
521 webkit_web_policy_decision_use(policy_decision);
526 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
531 /* If we can display it, let's display it... */
532 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
533 webkit_web_policy_decision_use (policy_decision);
537 /* ...everything we can't displayed is downloaded */
538 webkit_web_policy_decision_download (policy_decision);
542 /*@null@*/ WebKitWebView*
543 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
547 if (uzbl.state.selected_url != NULL) {
548 if (uzbl.state.verbose)
549 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
550 new_window_load_uri(uzbl.state.selected_url);
552 if (uzbl.state.verbose)
553 printf("New web view -> %s\n","Nothing to open, exiting");
559 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
562 if (uzbl.behave.download_handler) {
563 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
564 if (uzbl.state.verbose)
565 printf("Download -> %s\n",uri);
566 /* if urls not escaped, we may have to escape and quote uri before this call */
567 run_handler(uzbl.behave.download_handler, uri);
572 /* scroll a bar in a given direction */
574 scroll (GtkAdjustment* bar, GArray *argv) {
578 gdouble page_size = gtk_adjustment_get_page_size(bar);
579 gdouble value = gtk_adjustment_get_value(bar);
580 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
583 value += page_size * amount * 0.01;
587 max_value = gtk_adjustment_get_upper(bar) - page_size;
589 if (value > max_value)
590 value = max_value; /* don't scroll past the end of the page */
592 gtk_adjustment_set_value (bar, value);
596 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
597 (void) page; (void) argv; (void) result;
598 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
602 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
603 (void) page; (void) argv; (void) result;
604 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
605 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
609 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
610 (void) page; (void) result;
611 scroll(uzbl.gui.bar_v, argv);
615 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
616 (void) page; (void) result;
617 scroll(uzbl.gui.bar_h, argv);
622 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
623 if(uzbl.state.verbose)
624 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
626 /* update geometry var with the actual geometry
627 this is necessary as some WMs don't seem to honour
628 the above setting and we don't want to end up with
629 wrong geometry information
636 if (!uzbl.behave.show_status) {
637 gtk_widget_hide(uzbl.gui.mainbar);
639 gtk_widget_show(uzbl.gui.mainbar);
645 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
650 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
654 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
659 if (uzbl.behave.show_status) {
660 gtk_widget_hide(uzbl.gui.mainbar);
662 gtk_widget_show(uzbl.gui.mainbar);
664 uzbl.behave.show_status = !uzbl.behave.show_status;
669 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
673 //Set selected_url state variable
674 g_free(uzbl.state.selected_url);
675 uzbl.state.selected_url = NULL;
677 uzbl.state.selected_url = g_strdup(link);
683 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
686 const gchar *title = webkit_web_view_get_title(web_view);
687 if (uzbl.gui.main_title)
688 g_free (uzbl.gui.main_title);
689 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
694 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
697 uzbl.gui.sbar.load_progress = progress;
699 g_free(uzbl.gui.sbar.progress_bar);
700 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
706 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
710 if (uzbl.behave.load_finish_handler)
711 run_handler(uzbl.behave.load_finish_handler, "");
714 void clear_keycmd() {
715 g_free(uzbl.state.keycmd);
716 uzbl.state.keycmd = g_strdup("");
720 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
724 uzbl.gui.sbar.load_progress = 0;
725 clear_keycmd(); // don't need old commands to remain on new page?
726 if (uzbl.behave.load_start_handler)
727 run_handler(uzbl.behave.load_start_handler, "");
731 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
734 g_free (uzbl.state.uri);
735 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
736 uzbl.state.uri = g_string_free (newuri, FALSE);
737 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
738 set_insert_mode(uzbl.behave.always_insert_mode);
741 if (uzbl.behave.load_commit_handler)
742 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
746 destroy_cb (GtkWidget* widget, gpointer data) {
753 /* VIEW funcs (little webkit wrappers) */
754 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
756 VIEWFUNC(reload_bypass_cache)
757 VIEWFUNC(stop_loading)
764 /* -- command to callback/function map for things we cannot attach to any signals */
765 struct {const char *key; CommandInfo value;} cmdlist[] =
766 { /* key function no_split */
767 { "back", {view_go_back, 0} },
768 { "forward", {view_go_forward, 0} },
769 { "scroll_vert", {scroll_vert, 0} },
770 { "scroll_horz", {scroll_horz, 0} },
771 { "scroll_begin", {scroll_begin, 0} },
772 { "scroll_end", {scroll_end, 0} },
773 { "reload", {view_reload, 0}, },
774 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
775 { "stop", {view_stop_loading, 0}, },
776 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
777 { "zoom_out", {view_zoom_out, 0}, },
778 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
779 { "uri", {load_uri, TRUE} },
780 { "js", {run_js, TRUE} },
781 { "script", {run_external_js, 0} },
782 { "toggle_status", {toggle_status_cb, 0} },
783 { "spawn", {spawn, 0} },
784 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
785 { "sh", {spawn_sh, 0} },
786 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
787 { "talk_to_socket", {talk_to_socket, 0} },
788 { "exit", {close_uzbl, 0} },
789 { "search", {search_forward_text, TRUE} },
790 { "search_reverse", {search_reverse_text, TRUE} },
791 { "dehilight", {dehilight, 0} },
792 { "toggle_insert_mode", {toggle_insert_mode, 0} },
793 { "set", {set_var, TRUE} },
794 //{ "get", {get_var, TRUE} },
795 { "bind", {act_bind, TRUE} },
796 { "dump_config", {act_dump_config, 0} },
797 { "keycmd", {keycmd, TRUE} },
798 { "keycmd_nl", {keycmd_nl, TRUE} },
799 { "keycmd_bs", {keycmd_bs, 0} },
800 { "chain", {chain, 0} },
801 { "print", {print, TRUE} },
802 { "update_gui", {update_gui, TRUE} }
809 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
811 for (i = 0; i < LENGTH(cmdlist); i++)
812 g_hash_table_insert(uzbl.behave.commands, (gpointer) cmdlist[i].key, &cmdlist[i].value);
815 /* -- CORE FUNCTIONS -- */
818 free_action(gpointer act) {
819 Action *action = (Action*)act;
820 g_free(action->name);
822 g_free(action->param);
827 new_action(const gchar *name, const gchar *param) {
828 Action *action = g_new(Action, 1);
830 action->name = g_strdup(name);
832 action->param = g_strdup(param);
834 action->param = NULL;
840 file_exists (const char * filename) {
841 return (access(filename, F_OK) == 0);
845 set_var(WebKitWebView *page, GArray *argv, GString *result) {
846 (void) page; (void) result;
847 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
848 if (split[0] != NULL) {
849 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
850 set_var_value(g_strstrip(split[0]), value);
857 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
858 (void) page; (void) argv; (void) result;
864 print(WebKitWebView *page, GArray *argv, GString *result) {
865 (void) page; (void) result;
868 buf = expand(argv_idx(argv, 0), 0);
869 g_string_assign(result, buf);
874 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
875 (void) page; (void) result;
876 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
877 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
878 add_binding(g_strstrip(split[0]), value);
896 set_mode_indicator() {
897 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
898 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
903 set_mode_indicator();
908 set_insert_mode(gboolean mode) {
909 uzbl.behave.insert_mode = mode;
910 set_mode_indicator();
914 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
915 (void) page; (void) result;
917 if (argv_idx(argv, 0)) {
918 if (strcmp (argv_idx(argv, 0), "0") == 0) {
919 set_insert_mode(FALSE);
921 set_insert_mode(TRUE);
924 set_insert_mode( !uzbl.behave.insert_mode );
931 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
934 if (argv_idx(argv, 0)) {
935 GString* newuri = g_string_new (argv_idx(argv, 0));
936 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
937 run_js(web_view, argv, NULL);
940 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
941 g_string_prepend (newuri, "http://");
942 /* if we do handle cookies, ask our handler for them */
943 webkit_web_view_load_uri (web_view, newuri->str);
944 g_string_free (newuri, TRUE);
951 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
952 size_t argumentCount, const JSValueRef arguments[],
953 JSValueRef* exception) {
958 JSStringRef js_result_string;
959 GString *result = g_string_new("");
961 if (argumentCount >= 1) {
962 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
963 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
964 char ctl_line[arg_size];
965 JSStringGetUTF8CString(arg, ctl_line, arg_size);
967 parse_cmd_line(ctl_line, result);
969 JSStringRelease(arg);
971 js_result_string = JSStringCreateWithUTF8CString(result->str);
973 g_string_free(result, TRUE);
975 return JSValueMakeString(ctx, js_result_string);
978 JSStaticFunction js_static_functions[] = {
979 {"run", js_run_command, kJSPropertyAttributeNone},
984 /* This function creates the class and its definition, only once */
985 if (!uzbl.js.initialized) {
986 /* it would be pretty cool to make this dynamic */
987 uzbl.js.classdef = kJSClassDefinitionEmpty;
988 uzbl.js.classdef.staticFunctions = js_static_functions;
990 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
996 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
997 WebKitWebFrame *frame;
998 JSGlobalContextRef context;
999 JSObjectRef globalobject;
1000 JSStringRef var_name;
1002 JSStringRef js_script;
1003 JSValueRef js_result;
1004 JSStringRef js_result_string;
1005 size_t js_result_size;
1009 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1010 context = webkit_web_frame_get_global_context(frame);
1011 globalobject = JSContextGetGlobalObject(context);
1013 /* uzbl javascript namespace */
1014 var_name = JSStringCreateWithUTF8CString("Uzbl");
1015 JSObjectSetProperty(context, globalobject, var_name,
1016 JSObjectMake(context, uzbl.js.classref, NULL),
1017 kJSClassAttributeNone, NULL);
1019 /* evaluate the script and get return value*/
1020 js_script = JSStringCreateWithUTF8CString(script);
1021 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1022 if (js_result && !JSValueIsUndefined(context, js_result)) {
1023 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1024 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1026 if (js_result_size) {
1027 char js_result_utf8[js_result_size];
1028 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1029 g_string_assign(result, js_result_utf8);
1032 JSStringRelease(js_result_string);
1036 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1038 JSStringRelease(var_name);
1039 JSStringRelease(js_script);
1043 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1044 if (argv_idx(argv, 0))
1045 eval_js(web_view, argv_idx(argv, 0), result);
1049 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1051 if (argv_idx(argv, 0)) {
1052 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1057 while ((line = g_array_index(lines, gchar*, i))) {
1059 js = g_strdup (line);
1061 gchar* newjs = g_strconcat (js, line, NULL);
1068 if (uzbl.state.verbose)
1069 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1071 if (argv_idx (argv, 1)) {
1072 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1076 eval_js (web_view, js, result);
1078 g_array_free (lines, TRUE);
1083 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1084 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1085 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1086 webkit_web_view_unmark_text_matches (page);
1087 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1088 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1092 if (uzbl.state.searchtx) {
1093 if (uzbl.state.verbose)
1094 printf ("Searching: %s\n", uzbl.state.searchtx);
1095 webkit_web_view_set_highlight_text_matches (page, TRUE);
1096 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1101 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1103 search_text(page, argv, TRUE);
1107 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1109 search_text(page, argv, FALSE);
1113 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1114 (void) argv; (void) result;
1115 webkit_web_view_set_highlight_text_matches (page, FALSE);
1120 new_window_load_uri (const gchar * uri) {
1121 if (uzbl.behave.new_window) {
1122 GString *s = g_string_new ("");
1123 g_string_printf(s, "'%s'", uri);
1124 run_handler(uzbl.behave.new_window, s->str);
1127 GString* to_execute = g_string_new ("");
1128 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1130 for (i = 0; entries[i].long_name != NULL; i++) {
1131 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1132 gchar** str = (gchar**)entries[i].arg_data;
1134 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1138 if (uzbl.state.verbose)
1139 printf("\n%s\n", to_execute->str);
1140 g_spawn_command_line_async (to_execute->str, NULL);
1141 g_string_free (to_execute, TRUE);
1145 chain (WebKitWebView *page, GArray *argv, GString *result) {
1146 (void) page; (void) result;
1148 gchar **parts = NULL;
1150 while ((a = argv_idx(argv, i++))) {
1151 parts = g_strsplit (a, " ", 2);
1153 parse_command(parts[0], parts[1], result);
1159 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1163 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1169 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1173 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1179 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1184 int len = strlen(uzbl.state.keycmd);
1185 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1187 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1192 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1199 /* --Statusbar functions-- */
1201 build_progressbar_ascii(int percent) {
1202 int width=uzbl.gui.sbar.progress_w;
1205 GString *bar = g_string_new("");
1207 l = (double)percent*((double)width/100.);
1208 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1210 for(i=0; i<(int)l; i++)
1211 g_string_append(bar, uzbl.gui.sbar.progress_s);
1214 g_string_append(bar, uzbl.gui.sbar.progress_u);
1216 return g_string_free(bar, FALSE);
1218 /* --End Statusbar functions-- */
1221 sharg_append(GArray *a, const gchar *str) {
1222 const gchar *s = (str ? str : "");
1223 g_array_append_val(a, s);
1226 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1228 run_command (const gchar *command, const guint npre, const gchar **args,
1229 const gboolean sync, char **output_stdout) {
1230 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1233 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1234 gchar *pid = itos(getpid());
1235 gchar *xwin = itos(uzbl.xwin);
1237 sharg_append(a, command);
1238 for (i = 0; i < npre; i++) /* add n args before the default vars */
1239 sharg_append(a, args[i]);
1240 sharg_append(a, uzbl.state.config_file);
1241 sharg_append(a, pid);
1242 sharg_append(a, xwin);
1243 sharg_append(a, uzbl.comm.fifo_path);
1244 sharg_append(a, uzbl.comm.socket_path);
1245 sharg_append(a, uzbl.state.uri);
1246 sharg_append(a, uzbl.gui.main_title);
1248 for (i = npre; i < g_strv_length((gchar**)args); i++)
1249 sharg_append(a, args[i]);
1253 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1255 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1256 NULL, NULL, output_stdout, NULL, NULL, &err);
1257 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1258 NULL, NULL, NULL, &err);
1260 if (uzbl.state.verbose) {
1261 GString *s = g_string_new("spawned:");
1262 for (i = 0; i < (a->len); i++) {
1263 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1264 g_string_append_printf(s, " %s", qarg);
1267 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1268 printf("%s\n", s->str);
1269 g_string_free(s, TRUE);
1271 printf("Stdout: %s\n", *output_stdout);
1275 g_printerr("error on run_command: %s\n", err->message);
1280 g_array_free (a, TRUE);
1285 split_quoted(const gchar* src, const gboolean unquote) {
1286 /* split on unquoted space, return array of strings;
1287 remove a layer of quotes and backslashes if unquote */
1288 if (!src) return NULL;
1290 gboolean dq = FALSE;
1291 gboolean sq = FALSE;
1292 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1293 GString *s = g_string_new ("");
1297 for (p = src; *p != '\0'; p++) {
1298 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1299 else if (*p == '\\') { g_string_append_c(s, *p++);
1300 g_string_append_c(s, *p); }
1301 else if ((*p == '"') && unquote && !sq) dq = !dq;
1302 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1304 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1305 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1307 else if ((*p == ' ') && !dq && !sq) {
1308 dup = g_strdup(s->str);
1309 g_array_append_val(a, dup);
1310 g_string_truncate(s, 0);
1311 } else g_string_append_c(s, *p);
1313 dup = g_strdup(s->str);
1314 g_array_append_val(a, dup);
1315 ret = (gchar**)a->data;
1316 g_array_free (a, FALSE);
1317 g_string_free (s, TRUE);
1322 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1323 (void)web_view; (void)result;
1324 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1325 if (argv_idx(argv, 0))
1326 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1330 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1331 (void)web_view; (void)result;
1333 if (argv_idx(argv, 0))
1334 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1335 TRUE, &uzbl.comm.sync_stdout);
1339 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1340 (void)web_view; (void)result;
1341 if (!uzbl.behave.shell_cmd) {
1342 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1347 gchar *spacer = g_strdup("");
1348 g_array_insert_val(argv, 1, spacer);
1349 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1351 for (i = 1; i < g_strv_length(cmd); i++)
1352 g_array_prepend_val(argv, cmd[i]);
1354 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1360 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1361 (void)web_view; (void)result;
1362 if (!uzbl.behave.shell_cmd) {
1363 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1368 gchar *spacer = g_strdup("");
1369 g_array_insert_val(argv, 1, spacer);
1370 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1372 for (i = 1; i < g_strv_length(cmd); i++)
1373 g_array_prepend_val(argv, cmd[i]);
1375 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1376 TRUE, &uzbl.comm.sync_stdout);
1382 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1383 (void)web_view; (void)result;
1386 struct sockaddr_un sa;
1393 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1395 /* This function could be optimised by storing a hash table of socket paths
1396 and associated connected file descriptors rather than closing and
1397 re-opening for every call. Also we could launch a script if socket connect
1400 /* First element argv[0] is path to socket. Following elements are tokens to
1401 write to the socket. We write them as a single packet with each token
1402 separated by an ASCII nul (\0). */
1404 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1409 /* copy socket path, null terminate result */
1410 sockpath = g_array_index(argv, char*, 0);
1411 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1412 sa.sun_family = AF_UNIX;
1414 /* create socket file descriptor and connect it to path */
1415 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1417 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1420 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1421 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1426 /* build request vector */
1427 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1429 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1433 for(i = 1; i < argv->len; ++i) {
1434 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1435 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1439 ret = writev(fd, iov, argv->len - 1);
1442 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1447 /* wait for a response, with a 500ms timeout */
1449 pfd.events = POLLIN;
1451 ret = poll(&pfd, 1, 500);
1453 if(ret == 0) errno = ETIMEDOUT;
1454 if(errno == EINTR) continue;
1455 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1461 /* get length of response */
1462 if(ioctl(fd, FIONREAD, &len) == -1) {
1463 g_printerr("talk_to_socket: cannot find daemon response length, "
1464 "ioctl failed (%s)\n", strerror(errno));
1469 /* if there is a response, read it */
1471 uzbl.comm.sync_stdout = g_malloc(len + 1);
1472 if(!uzbl.comm.sync_stdout) {
1473 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1477 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1479 ret = read(fd, uzbl.comm.sync_stdout, len);
1481 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1494 parse_command(const char *cmd, const char *param, GString *result) {
1497 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1499 gchar **par = split_quoted(param, TRUE);
1500 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1502 if (c->no_split) { /* don't split */
1503 sharg_append(a, param);
1505 for (i = 0; i < g_strv_length(par); i++)
1506 sharg_append(a, par[i]);
1509 if (result == NULL) {
1510 GString *result_print = g_string_new("");
1512 c->function(uzbl.gui.web_view, a, result_print);
1513 if (result_print->len)
1514 printf("%*s\n", (int)result_print->len, result_print->str);
1516 g_string_free(result_print, TRUE);
1518 c->function(uzbl.gui.web_view, a, result);
1521 g_array_free (a, TRUE);
1524 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1531 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
1532 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1533 (GType) SOUP_SESSION_PROXY_URI);
1536 suri = soup_uri_new(uzbl.net.proxy_url);
1537 g_object_set(G_OBJECT(uzbl.net.soup_session),
1538 SOUP_SESSION_PROXY_URI,
1540 soup_uri_free(suri);
1547 if(file_exists(uzbl.gui.icon)) {
1548 if (uzbl.gui.main_window)
1549 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1551 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1557 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1558 g_array_append_val (a, uzbl.state.uri);
1559 load_uri(uzbl.gui.web_view, a, NULL);
1560 g_array_free (a, TRUE);
1564 cmd_always_insert_mode() {
1565 set_insert_mode(uzbl.behave.always_insert_mode);
1571 g_object_set(G_OBJECT(uzbl.net.soup_session),
1572 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1576 cmd_max_conns_host() {
1577 g_object_set(G_OBJECT(uzbl.net.soup_session),
1578 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1583 soup_session_remove_feature
1584 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1585 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1586 /*g_free(uzbl.net.soup_logger);*/
1588 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1589 soup_session_add_feature(uzbl.net.soup_session,
1590 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1595 return webkit_web_view_get_settings(uzbl.gui.web_view);
1600 WebKitWebSettings *ws = view_settings();
1601 if (uzbl.behave.font_size > 0) {
1602 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1605 if (uzbl.behave.monospace_size > 0) {
1606 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1607 uzbl.behave.monospace_size, NULL);
1609 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1610 uzbl.behave.font_size, NULL);
1615 cmd_default_font_family() {
1616 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1617 uzbl.behave.default_font_family, NULL);
1621 cmd_monospace_font_family() {
1622 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1623 uzbl.behave.monospace_font_family, NULL);
1627 cmd_sans_serif_font_family() {
1628 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1629 uzbl.behave.sans_serif_font_family, NULL);
1633 cmd_serif_font_family() {
1634 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1635 uzbl.behave.serif_font_family, NULL);
1639 cmd_cursive_font_family() {
1640 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1641 uzbl.behave.cursive_font_family, NULL);
1645 cmd_fantasy_font_family() {
1646 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1647 uzbl.behave.fantasy_font_family, NULL);
1652 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1656 cmd_disable_plugins() {
1657 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1658 !uzbl.behave.disable_plugins, NULL);
1662 cmd_disable_scripts() {
1663 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1664 !uzbl.behave.disable_scripts, NULL);
1668 cmd_minimum_font_size() {
1669 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1670 uzbl.behave.minimum_font_size, NULL);
1673 cmd_autoload_img() {
1674 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1675 uzbl.behave.autoload_img, NULL);
1680 cmd_autoshrink_img() {
1681 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1682 uzbl.behave.autoshrink_img, NULL);
1687 cmd_enable_spellcheck() {
1688 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1689 uzbl.behave.enable_spellcheck, NULL);
1693 cmd_enable_private() {
1694 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1695 uzbl.behave.enable_private, NULL);
1700 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1701 uzbl.behave.print_bg, NULL);
1706 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1707 uzbl.behave.style_uri, NULL);
1711 cmd_resizable_txt() {
1712 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1713 uzbl.behave.resizable_txt, NULL);
1717 cmd_default_encoding() {
1718 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1719 uzbl.behave.default_encoding, NULL);
1723 cmd_enforce_96dpi() {
1724 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1725 uzbl.behave.enforce_96dpi, NULL);
1729 cmd_caret_browsing() {
1730 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1731 uzbl.behave.caret_browsing, NULL);
1735 cmd_cookie_handler() {
1736 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1737 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1738 if ((g_strcmp0(split[0], "sh") == 0) ||
1739 (g_strcmp0(split[0], "spawn") == 0)) {
1740 g_free (uzbl.behave.cookie_handler);
1741 uzbl.behave.cookie_handler =
1742 g_strdup_printf("sync_%s %s", split[0], split[1]);
1749 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1750 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1751 if ((g_strcmp0(split[0], "sh") == 0) ||
1752 (g_strcmp0(split[0], "spawn") == 0)) {
1753 g_free (uzbl.behave.new_window);
1754 uzbl.behave.new_window =
1755 g_strdup_printf("%s %s", split[0], split[1]);
1762 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1767 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1772 if(uzbl.behave.inject_html) {
1773 webkit_web_view_load_html_string (uzbl.gui.web_view,
1774 uzbl.behave.inject_html, NULL);
1783 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1784 uzbl.behave.modmask = 0;
1786 if(uzbl.behave.modkey)
1787 g_free(uzbl.behave.modkey);
1788 uzbl.behave.modkey = buf;
1790 for (i = 0; modkeys[i].key != NULL; i++) {
1791 if (g_strrstr(buf, modkeys[i].key))
1792 uzbl.behave.modmask |= modkeys[i].mask;
1798 if (*uzbl.net.useragent == ' ') {
1799 g_free (uzbl.net.useragent);
1800 uzbl.net.useragent = NULL;
1802 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1803 uzbl.net.useragent, NULL);
1809 if (!uzbl.gui.scrolled_win &&
1813 gtk_widget_ref(uzbl.gui.scrolled_win);
1814 gtk_widget_ref(uzbl.gui.mainbar);
1815 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1816 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1818 if(uzbl.behave.status_top) {
1819 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1820 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1823 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1824 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1826 gtk_widget_unref(uzbl.gui.scrolled_win);
1827 gtk_widget_unref(uzbl.gui.mainbar);
1828 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1833 set_var_value(const gchar *name, gchar *val) {
1834 uzbl_cmdprop *c = NULL;
1837 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1839 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1840 if(!c->writeable) return FALSE;
1842 /* check for the variable type */
1843 if (c->type == TYPE_STR) {
1844 buf = expand(val, 0);
1847 } else if(c->type == TYPE_INT) {
1848 buf = expand(val, 0);
1849 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1851 } else if (c->type == TYPE_FLOAT) {
1852 buf = expand(val, 0);
1853 *c->ptr.f = strtod(buf, &endp);
1857 /* invoke a command specific function */
1858 if(c->func) c->func();
1860 /* check wether name violates our naming scheme */
1861 if(strpbrk(name, invalid_chars)) {
1862 if (uzbl.state.verbose)
1863 printf("Invalid variable name\n");
1868 c = malloc(sizeof(uzbl_cmdprop));
1873 buf = expand(val, 0);
1874 c->ptr.s = malloc(sizeof(char *));
1876 g_hash_table_insert(uzbl.comm.proto_var,
1877 g_strdup(name), (gpointer) c);
1882 enum {M_CMD, M_HTML};
1884 parse_cmd_line(const char *ctl_line, GString *result) {
1887 if((ctl_line[0] == '#') /* Comments */
1888 || (ctl_line[0] == ' ')
1889 || (ctl_line[0] == '\n'))
1890 ; /* ignore these lines */
1891 else { /* parse a command */
1893 gchar **tokens = NULL;
1894 len = strlen(ctl_line);
1896 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1897 ctlstrip = g_strndup(ctl_line, len - 1);
1898 else ctlstrip = g_strdup(ctl_line);
1900 tokens = g_strsplit(ctlstrip, " ", 2);
1901 parse_command(tokens[0], tokens[1], result);
1908 build_stream_name(int type, const gchar* dir) {
1909 State *s = &uzbl.state;
1913 str = g_strdup_printf
1914 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1915 } else if (type == SOCKET) {
1916 str = g_strdup_printf
1917 ("%s/uzbl_socket_%s", dir, s->instance_name);
1923 control_fifo(GIOChannel *gio, GIOCondition condition) {
1924 if (uzbl.state.verbose)
1925 printf("triggered\n");
1930 if (condition & G_IO_HUP)
1931 g_error ("Fifo: Read end of pipe died!\n");
1934 g_error ("Fifo: GIOChannel broke\n");
1936 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1937 if (ret == G_IO_STATUS_ERROR) {
1938 g_error ("Fifo: Error reading: %s\n", err->message);
1942 parse_cmd_line(ctl_line, NULL);
1949 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1950 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1951 if (unlink(uzbl.comm.fifo_path) == -1)
1952 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1953 g_free(uzbl.comm.fifo_path);
1954 uzbl.comm.fifo_path = NULL;
1957 GIOChannel *chan = NULL;
1958 GError *error = NULL;
1959 gchar *path = build_stream_name(FIFO, dir);
1961 if (!file_exists(path)) {
1962 if (mkfifo (path, 0666) == 0) {
1963 // 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.
1964 chan = g_io_channel_new_file(path, "r+", &error);
1966 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1967 if (uzbl.state.verbose)
1968 printf ("init_fifo: created successfully as %s\n", path);
1969 uzbl.comm.fifo_path = path;
1971 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1972 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1973 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1974 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1976 /* if we got this far, there was an error; cleanup */
1977 if (error) g_error_free (error);
1984 control_stdin(GIOChannel *gio, GIOCondition condition) {
1986 gchar *ctl_line = NULL;
1989 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1990 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1993 parse_cmd_line(ctl_line, NULL);
2001 GIOChannel *chan = NULL;
2002 GError *error = NULL;
2004 chan = g_io_channel_unix_new(fileno(stdin));
2006 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2007 g_error ("Stdin: could not add watch\n");
2009 if (uzbl.state.verbose)
2010 printf ("Stdin: watch added successfully\n");
2013 g_error ("Stdin: Error while opening: %s\n", error->message);
2015 if (error) g_error_free (error);
2019 control_socket(GIOChannel *chan) {
2020 struct sockaddr_un remote;
2021 unsigned int t = sizeof(remote);
2023 GIOChannel *clientchan;
2025 clientsock = accept (g_io_channel_unix_get_fd(chan),
2026 (struct sockaddr *) &remote, &t);
2028 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2029 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2030 (GIOFunc) control_client_socket, clientchan);
2037 control_client_socket(GIOChannel *clientchan) {
2039 GString *result = g_string_new("");
2040 GError *error = NULL;
2044 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2045 if (ret == G_IO_STATUS_ERROR) {
2046 g_warning ("Error reading: %s\n", error->message);
2047 g_io_channel_shutdown(clientchan, TRUE, &error);
2049 } else if (ret == G_IO_STATUS_EOF) {
2050 /* shutdown and remove channel watch from main loop */
2051 g_io_channel_shutdown(clientchan, TRUE, &error);
2056 parse_cmd_line (ctl_line, result);
2057 g_string_append_c(result, '\n');
2058 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2060 if (ret == G_IO_STATUS_ERROR) {
2061 g_warning ("Error writing: %s", error->message);
2063 g_io_channel_flush(clientchan, &error);
2066 if (error) g_error_free (error);
2067 g_string_free(result, TRUE);
2073 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2074 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2075 if (unlink(uzbl.comm.socket_path) == -1)
2076 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2077 g_free(uzbl.comm.socket_path);
2078 uzbl.comm.socket_path = NULL;
2086 GIOChannel *chan = NULL;
2088 struct sockaddr_un local;
2089 gchar *path = build_stream_name(SOCKET, dir);
2091 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2093 local.sun_family = AF_UNIX;
2094 strcpy (local.sun_path, path);
2095 unlink (local.sun_path);
2097 len = strlen (local.sun_path) + sizeof (local.sun_family);
2098 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2099 if (uzbl.state.verbose)
2100 printf ("init_socket: opened in %s\n", path);
2103 if( (chan = g_io_channel_unix_new(sock)) ) {
2104 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2105 uzbl.comm.socket_path = path;
2108 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2110 /* if we got this far, there was an error; cleanup */
2117 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2118 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2120 // this function may be called very early when the templates are not set (yet), hence the checks
2122 update_title (void) {
2123 Behaviour *b = &uzbl.behave;
2126 if (b->show_status) {
2127 if (b->title_format_short) {
2128 parsed = expand(b->title_format_short, 0);
2129 if (uzbl.gui.main_window)
2130 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2133 if (b->status_format) {
2134 parsed = expand(b->status_format, 0);
2135 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2138 if (b->status_background) {
2140 gdk_color_parse (b->status_background, &color);
2141 //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)
2142 if (uzbl.gui.main_window)
2143 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2144 else if (uzbl.gui.plug)
2145 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2148 if (b->title_format_long) {
2149 parsed = expand(b->title_format_long, 0);
2150 if (uzbl.gui.main_window)
2151 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2158 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2162 retrieve_geometry();
2167 key_press_cb (GtkWidget* window, GdkEventKey* event)
2169 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2173 if (event->type != GDK_KEY_PRESS ||
2174 event->keyval == GDK_Page_Up ||
2175 event->keyval == GDK_Page_Down ||
2176 event->keyval == GDK_Up ||
2177 event->keyval == GDK_Down ||
2178 event->keyval == GDK_Left ||
2179 event->keyval == GDK_Right ||
2180 event->keyval == GDK_Shift_L ||
2181 event->keyval == GDK_Shift_R)
2184 /* turn off insert mode (if always_insert_mode is not used) */
2185 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2186 set_insert_mode(uzbl.behave.always_insert_mode);
2191 if (uzbl.behave.insert_mode &&
2192 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2193 (!uzbl.behave.modmask)
2198 if (event->keyval == GDK_Escape) {
2201 dehilight(uzbl.gui.web_view, NULL, NULL);
2205 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2206 if (event->keyval == GDK_Insert) {
2208 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2209 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2211 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2214 GString* keycmd = g_string_new(uzbl.state.keycmd);
2215 g_string_append (keycmd, str);
2216 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2223 if (event->keyval == GDK_BackSpace)
2224 keycmd_bs(NULL, NULL, NULL);
2226 gboolean key_ret = FALSE;
2227 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2230 GString* keycmd = g_string_new(uzbl.state.keycmd);
2231 g_string_append(keycmd, event->string);
2232 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2235 run_keycmd(key_ret);
2237 if (key_ret) return (!uzbl.behave.insert_mode);
2242 run_keycmd(const gboolean key_ret) {
2243 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2245 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2247 parse_command(act->name, act->param, NULL);
2251 /* try if it's an incremental keycmd or one that takes args, and run it */
2252 GString* short_keys = g_string_new ("");
2253 GString* short_keys_inc = g_string_new ("");
2255 guint len = strlen(uzbl.state.keycmd);
2256 for (i=0; i<len; i++) {
2257 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2258 g_string_assign(short_keys_inc, short_keys->str);
2259 g_string_append_c(short_keys, '_');
2260 g_string_append_c(short_keys_inc, '*');
2262 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2263 /* run normal cmds only if return was pressed */
2264 exec_paramcmd(act, i);
2267 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2268 if (key_ret) /* just quit the incremental command on return */
2270 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2274 g_string_truncate(short_keys, short_keys->len - 1);
2276 g_string_free (short_keys, TRUE);
2277 g_string_free (short_keys_inc, TRUE);
2281 exec_paramcmd(const Action *act, const guint i) {
2282 GString *parampart = g_string_new (uzbl.state.keycmd);
2283 GString *actionname = g_string_new ("");
2284 GString *actionparam = g_string_new ("");
2285 g_string_erase (parampart, 0, i+1);
2287 g_string_printf (actionname, act->name, parampart->str);
2289 g_string_printf (actionparam, act->param, parampart->str);
2290 parse_command(actionname->str, actionparam->str, NULL);
2291 g_string_free(actionname, TRUE);
2292 g_string_free(actionparam, TRUE);
2293 g_string_free(parampart, TRUE);
2301 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2303 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2304 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2305 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2306 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2307 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2308 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2309 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2310 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2311 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2312 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2319 g->mainbar = gtk_hbox_new (FALSE, 0);
2321 /* keep a reference to the bar so we can re-pack it at runtime*/
2322 //sbar_ref = g_object_ref(g->mainbar);
2324 g->mainbar_label = gtk_label_new ("");
2325 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2326 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2327 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2328 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2329 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2330 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2336 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2337 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2338 gtk_widget_set_name (window, "Uzbl browser");
2339 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2340 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2341 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2348 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2349 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2350 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2357 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2359 If actname is one that calls an external command, this function will inject
2360 newargs in front of the user-provided args in that command line. They will
2361 come become after the body of the script (in sh) or after the name of
2362 the command to execute (in spawn).
2363 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2364 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2366 The return value consist of two strings: the action (sh, ...) and its args.
2368 If act is not one that calls an external command, then the given action merely
2371 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2372 /* Arrr! Here be memory leaks */
2373 gchar *actdup = g_strdup(actname);
2374 g_array_append_val(rets, actdup);
2376 if ((g_strcmp0(actname, "spawn") == 0) ||
2377 (g_strcmp0(actname, "sh") == 0) ||
2378 (g_strcmp0(actname, "sync_spawn") == 0) ||
2379 (g_strcmp0(actname, "sync_sh") == 0) ||
2380 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2382 GString *a = g_string_new("");
2383 gchar **spawnparts = split_quoted(origargs, FALSE);
2384 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2385 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2387 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2388 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2390 g_array_append_val(rets, a->str);
2391 g_string_free(a, FALSE);
2392 g_strfreev(spawnparts);
2394 gchar *origdup = g_strdup(origargs);
2395 g_array_append_val(rets, origdup);
2397 return (gchar**)g_array_free(rets, FALSE);
2401 run_handler (const gchar *act, const gchar *args) {
2402 /* Consider this code a temporary hack to make the handlers usable.
2403 In practice, all this splicing, injection, and reconstruction is
2404 inefficient, annoying and hard to manage. Potential pitfalls arise
2405 when the handler specific args 1) are not quoted (the handler
2406 callbacks should take care of this) 2) are quoted but interfere
2407 with the users' own quotation. A more ideal solution is
2408 to refactor parse_command so that it doesn't just take a string
2409 and execute it; rather than that, we should have a function which
2410 returns the argument vector parsed from the string. This vector
2411 could be modified (e.g. insert additional args into it) before
2412 passing it to the next function that actually executes it. Though
2413 it still isn't perfect for chain actions.. will reconsider & re-
2414 factor when I have the time. -duc */
2416 char **parts = g_strsplit(act, " ", 2);
2418 if (g_strcmp0(parts[0], "chain") == 0) {
2419 GString *newargs = g_string_new("");
2420 gchar **chainparts = split_quoted(parts[1], FALSE);
2422 /* for every argument in the chain, inject the handler args
2423 and make sure the new parts are wrapped in quotes */
2424 gchar **cp = chainparts;
2426 gchar *quotless = NULL;
2427 gchar **spliced_quotless = NULL; // sigh -_-;
2428 gchar **inpart = NULL;
2431 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2433 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2434 } else quotless = g_strdup(*cp);
2436 spliced_quotless = g_strsplit(quotless, " ", 2);
2437 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2438 g_strfreev(spliced_quotless);
2440 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2446 parse_command(parts[0], &(newargs->str[1]), NULL);
2447 g_string_free(newargs, TRUE);
2448 g_strfreev(chainparts);
2451 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2452 parse_command(inparts[0], inparts[1], NULL);
2460 add_binding (const gchar *key, const gchar *act) {
2461 char **parts = g_strsplit(act, " ", 2);
2468 if (uzbl.state.verbose)
2469 printf ("Binding %-10s : %s\n", key, act);
2470 action = new_action(parts[0], parts[1]);
2472 if (g_hash_table_remove (uzbl.bindings, key))
2473 g_warning ("Overwriting existing binding for \"%s\"", key);
2474 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2479 get_xdg_var (XDG_Var xdg) {
2480 const gchar* actual_value = getenv (xdg.environmental);
2481 const gchar* home = getenv ("HOME");
2482 gchar* return_value;
2484 if (! actual_value || strcmp (actual_value, "") == 0) {
2485 if (xdg.default_value) {
2486 return_value = str_replace ("~", home, xdg.default_value);
2488 return_value = NULL;
2491 return_value = str_replace("~", home, actual_value);
2494 return return_value;
2498 find_xdg_file (int xdg_type, const char* filename) {
2499 /* xdg_type = 0 => config
2500 xdg_type = 1 => data
2501 xdg_type = 2 => cache*/
2503 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2504 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2507 gchar* temporary_string;
2511 if (! file_exists (temporary_file) && xdg_type != 2) {
2512 buf = get_xdg_var (XDG[3 + xdg_type]);
2513 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2516 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2517 g_free (temporary_file);
2518 temporary_file = g_strconcat (temporary_string, filename, NULL);
2522 //g_free (temporary_string); - segfaults.
2524 if (file_exists (temporary_file)) {
2525 return temporary_file;
2527 g_free(temporary_file);
2533 State *s = &uzbl.state;
2534 Network *n = &uzbl.net;
2536 for (i = 0; default_config[i].command != NULL; i++) {
2537 parse_cmd_line(default_config[i].command, NULL);
2540 if (g_strcmp0(s->config_file, "-") == 0) {
2541 s->config_file = NULL;
2545 else if (!s->config_file) {
2546 s->config_file = find_xdg_file (0, "/uzbl/config");
2549 if (s->config_file) {
2550 GArray* lines = read_file_by_line (s->config_file);
2554 while ((line = g_array_index(lines, gchar*, i))) {
2555 parse_cmd_line (line, NULL);
2559 g_array_free (lines, TRUE);
2561 if (uzbl.state.verbose)
2562 printf ("No configuration file loaded.\n");
2565 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2568 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2571 if (!uzbl.behave.cookie_handler)
2574 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2575 GString *s = g_string_new ("");
2576 SoupURI * soup_uri = soup_message_get_uri(msg);
2577 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2578 run_handler(uzbl.behave.cookie_handler, s->str);
2580 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2581 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2582 if ( p != NULL ) *p = '\0';
2583 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2585 if (uzbl.comm.sync_stdout)
2586 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2588 g_string_free(s, TRUE);
2592 save_cookies (SoupMessage *msg, gpointer user_data){
2596 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2597 cookie = soup_cookie_to_set_cookie_header(ck->data);
2598 SoupURI * soup_uri = soup_message_get_uri(msg);
2599 GString *s = g_string_new ("");
2600 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2601 run_handler(uzbl.behave.cookie_handler, s->str);
2603 g_string_free(s, TRUE);
2608 /* --- WEBINSPECTOR --- */
2610 hide_window_cb(GtkWidget *widget, gpointer data) {
2613 gtk_widget_hide(widget);
2617 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2620 (void) web_inspector;
2621 GtkWidget* scrolled_window;
2622 GtkWidget* new_web_view;
2625 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2626 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2627 G_CALLBACK(hide_window_cb), NULL);
2629 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2630 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2631 gtk_widget_show(g->inspector_window);
2633 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2634 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2635 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2636 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2637 gtk_widget_show(scrolled_window);
2639 new_web_view = webkit_web_view_new();
2640 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2642 return WEBKIT_WEB_VIEW(new_web_view);
2646 inspector_show_window_cb (WebKitWebInspector* inspector){
2648 gtk_widget_show(uzbl.gui.inspector_window);
2652 /* TODO: Add variables and code to make use of these functions */
2654 inspector_close_window_cb (WebKitWebInspector* inspector){
2660 inspector_attach_window_cb (WebKitWebInspector* inspector){
2666 inspector_detach_window_cb (WebKitWebInspector* inspector){
2672 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2678 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2684 set_up_inspector() {
2686 WebKitWebSettings *settings = view_settings();
2687 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2689 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2690 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2691 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2692 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2693 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2694 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2695 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2697 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2701 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2703 uzbl_cmdprop *c = v;
2708 if(c->type == TYPE_STR)
2709 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2710 else if(c->type == TYPE_INT)
2711 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2712 else if(c->type == TYPE_FLOAT)
2713 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2717 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2721 printf("bind %s = %s %s\n", (char *)k ,
2722 (char *)a->name, a->param?(char *)a->param:"");
2727 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2728 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2732 retrieve_geometry() {
2734 GString *buf = g_string_new("");
2736 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2737 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2739 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2741 if(uzbl.gui.geometry)
2742 g_free(uzbl.gui.geometry);
2743 uzbl.gui.geometry = g_string_free(buf, FALSE);
2746 /* set up gtk, gobject, variable defaults and other things that tests and other
2747 * external applications need to do anyhow */
2749 initialize(int argc, char *argv[]) {
2750 if (!g_thread_supported ())
2751 g_thread_init (NULL);
2752 uzbl.state.executable_path = g_strdup(argv[0]);
2753 uzbl.state.selected_url = NULL;
2754 uzbl.state.searchtx = NULL;
2756 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2757 g_option_context_add_main_entries (context, entries, NULL);
2758 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2759 g_option_context_parse (context, &argc, &argv, NULL);
2760 g_option_context_free(context);
2762 if (uzbl.behave.print_version) {
2763 printf("Commit: %s\n", COMMIT);
2767 /* initialize hash table */
2768 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2770 uzbl.net.soup_session = webkit_get_default_session();
2771 uzbl.state.keycmd = g_strdup("");
2773 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2774 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2775 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2776 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2778 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2779 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2780 uzbl.gui.sbar.progress_w = 10;
2782 /* default mode indicators */
2783 uzbl.behave.insert_indicator = g_strdup("I");
2784 uzbl.behave.cmd_indicator = g_strdup("C");
2786 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2787 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2788 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2789 uzbl.info.arch = ARCH;
2790 uzbl.info.commit = COMMIT;
2793 make_var_to_name_hash();
2798 #ifndef UZBL_LIBRARY
2801 main (int argc, char* argv[]) {
2802 initialize(argc, argv);
2804 gtk_init (&argc, &argv);
2806 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2807 //main_window_ref = g_object_ref(scrolled_window);
2808 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2809 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2811 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2812 GTK_WIDGET (uzbl.gui.web_view));
2814 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2818 /* initial packing */
2819 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2820 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2822 if (uzbl.state.socket_id) {
2823 uzbl.gui.plug = create_plug ();
2824 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2825 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2827 uzbl.gui.main_window = create_window ();
2828 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2829 gtk_widget_show_all (uzbl.gui.main_window);
2830 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2833 if(!uzbl.state.instance_name)
2834 uzbl.state.instance_name = itos((int)uzbl.xwin);
2836 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2838 if (uzbl.state.verbose) {
2839 printf("Uzbl start location: %s\n", argv[0]);
2840 if (uzbl.state.socket_id)
2841 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2843 printf("window_id %i\n",(int) uzbl.xwin);
2844 printf("pid %i\n", getpid ());
2845 printf("name: %s\n", uzbl.state.instance_name);
2848 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2849 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2850 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2851 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2852 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2854 /* Check uzbl is in window mode before getting/setting geometry */
2855 if (uzbl.gui.main_window) {
2856 if(uzbl.gui.geometry)
2859 retrieve_geometry();
2862 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2863 if (argc > 1 && !uzbl.state.uri)
2864 uri_override = g_strdup(argv[1]);
2865 gboolean verbose_override = uzbl.state.verbose;
2868 set_insert_mode(FALSE);
2870 if (!uzbl.behave.show_status)
2871 gtk_widget_hide(uzbl.gui.mainbar);
2878 if (verbose_override > uzbl.state.verbose)
2879 uzbl.state.verbose = verbose_override;
2882 set_var_value("uri", uri_override);
2883 g_free(uri_override);
2884 } else if (uzbl.state.uri)
2890 return EXIT_SUCCESS;
2894 /* vi: set et ts=4: */