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 if (uzbl.behave.load_start_handler)
726 run_handler(uzbl.behave.load_start_handler, "");
730 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
733 g_free (uzbl.state.uri);
734 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
735 uzbl.state.uri = g_string_free (newuri, FALSE);
736 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
737 set_insert_mode(uzbl.behave.always_insert_mode);
740 if (uzbl.behave.load_commit_handler)
741 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
745 destroy_cb (GtkWidget* widget, gpointer data) {
752 /* VIEW funcs (little webkit wrappers) */
753 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
755 VIEWFUNC(reload_bypass_cache)
756 VIEWFUNC(stop_loading)
763 /* -- command to callback/function map for things we cannot attach to any signals */
764 struct {const char *key; CommandInfo value;} cmdlist[] =
765 { /* key function no_split */
766 { "back", {view_go_back, 0} },
767 { "forward", {view_go_forward, 0} },
768 { "scroll_vert", {scroll_vert, 0} },
769 { "scroll_horz", {scroll_horz, 0} },
770 { "scroll_begin", {scroll_begin, 0} },
771 { "scroll_end", {scroll_end, 0} },
772 { "reload", {view_reload, 0}, },
773 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
774 { "stop", {view_stop_loading, 0}, },
775 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
776 { "zoom_out", {view_zoom_out, 0}, },
777 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
778 { "uri", {load_uri, TRUE} },
779 { "js", {run_js, TRUE} },
780 { "script", {run_external_js, 0} },
781 { "toggle_status", {toggle_status_cb, 0} },
782 { "spawn", {spawn, 0} },
783 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
784 { "sh", {spawn_sh, 0} },
785 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
786 { "talk_to_socket", {talk_to_socket, 0} },
787 { "exit", {close_uzbl, 0} },
788 { "search", {search_forward_text, TRUE} },
789 { "search_reverse", {search_reverse_text, TRUE} },
790 { "dehilight", {dehilight, 0} },
791 { "toggle_insert_mode", {toggle_insert_mode, 0} },
792 { "set", {set_var, TRUE} },
793 //{ "get", {get_var, TRUE} },
794 { "bind", {act_bind, TRUE} },
795 { "dump_config", {act_dump_config, 0} },
796 { "keycmd", {keycmd, TRUE} },
797 { "keycmd_nl", {keycmd_nl, TRUE} },
798 { "keycmd_bs", {keycmd_bs, 0} },
799 { "chain", {chain, 0} },
800 { "print", {print, TRUE} },
801 { "update_gui", {update_gui, TRUE} }
808 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
810 for (i = 0; i < LENGTH(cmdlist); i++)
811 g_hash_table_insert(uzbl.behave.commands, (gpointer) cmdlist[i].key, &cmdlist[i].value);
814 /* -- CORE FUNCTIONS -- */
817 free_action(gpointer act) {
818 Action *action = (Action*)act;
819 g_free(action->name);
821 g_free(action->param);
826 new_action(const gchar *name, const gchar *param) {
827 Action *action = g_new(Action, 1);
829 action->name = g_strdup(name);
831 action->param = g_strdup(param);
833 action->param = NULL;
839 file_exists (const char * filename) {
840 return (access(filename, F_OK) == 0);
844 set_var(WebKitWebView *page, GArray *argv, GString *result) {
845 (void) page; (void) result;
846 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
847 if (split[0] != NULL) {
848 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
849 set_var_value(g_strstrip(split[0]), value);
856 update_gui(WebKitWebView *page, GArray *argv, GString *result) {
857 (void) page; (void) argv; (void) result;
863 print(WebKitWebView *page, GArray *argv, GString *result) {
864 (void) page; (void) result;
867 buf = expand(argv_idx(argv, 0), 0);
868 g_string_assign(result, buf);
873 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
874 (void) page; (void) result;
875 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
876 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
877 add_binding(g_strstrip(split[0]), value);
895 set_mode_indicator() {
896 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
897 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
902 set_mode_indicator();
907 set_insert_mode(gboolean mode) {
908 uzbl.behave.insert_mode = mode;
909 set_mode_indicator();
913 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
914 (void) page; (void) result;
916 if (argv_idx(argv, 0)) {
917 if (strcmp (argv_idx(argv, 0), "0") == 0) {
918 set_insert_mode(FALSE);
920 set_insert_mode(TRUE);
923 set_insert_mode( !uzbl.behave.insert_mode );
930 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
933 if (argv_idx(argv, 0)) {
934 GString* newuri = g_string_new (argv_idx(argv, 0));
935 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
936 run_js(web_view, argv, NULL);
939 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
940 g_string_prepend (newuri, "http://");
941 /* if we do handle cookies, ask our handler for them */
942 webkit_web_view_load_uri (web_view, newuri->str);
943 g_string_free (newuri, TRUE);
950 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
951 size_t argumentCount, const JSValueRef arguments[],
952 JSValueRef* exception) {
957 JSStringRef js_result_string;
958 GString *result = g_string_new("");
960 if (argumentCount >= 1) {
961 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
962 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
963 char ctl_line[arg_size];
964 JSStringGetUTF8CString(arg, ctl_line, arg_size);
966 parse_cmd_line(ctl_line, result);
968 JSStringRelease(arg);
970 js_result_string = JSStringCreateWithUTF8CString(result->str);
972 g_string_free(result, TRUE);
974 return JSValueMakeString(ctx, js_result_string);
977 JSStaticFunction js_static_functions[] = {
978 {"run", js_run_command, kJSPropertyAttributeNone},
983 /* This function creates the class and its definition, only once */
984 if (!uzbl.js.initialized) {
985 /* it would be pretty cool to make this dynamic */
986 uzbl.js.classdef = kJSClassDefinitionEmpty;
987 uzbl.js.classdef.staticFunctions = js_static_functions;
989 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
995 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
996 WebKitWebFrame *frame;
997 JSGlobalContextRef context;
998 JSObjectRef globalobject;
999 JSStringRef var_name;
1001 JSStringRef js_script;
1002 JSValueRef js_result;
1003 JSStringRef js_result_string;
1004 size_t js_result_size;
1008 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1009 context = webkit_web_frame_get_global_context(frame);
1010 globalobject = JSContextGetGlobalObject(context);
1012 /* uzbl javascript namespace */
1013 var_name = JSStringCreateWithUTF8CString("Uzbl");
1014 JSObjectSetProperty(context, globalobject, var_name,
1015 JSObjectMake(context, uzbl.js.classref, NULL),
1016 kJSClassAttributeNone, NULL);
1018 /* evaluate the script and get return value*/
1019 js_script = JSStringCreateWithUTF8CString(script);
1020 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1021 if (js_result && !JSValueIsUndefined(context, js_result)) {
1022 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1023 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1025 if (js_result_size) {
1026 char js_result_utf8[js_result_size];
1027 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1028 g_string_assign(result, js_result_utf8);
1031 JSStringRelease(js_result_string);
1035 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1037 JSStringRelease(var_name);
1038 JSStringRelease(js_script);
1042 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1043 if (argv_idx(argv, 0))
1044 eval_js(web_view, argv_idx(argv, 0), result);
1048 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1050 if (argv_idx(argv, 0)) {
1051 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1056 while ((line = g_array_index(lines, gchar*, i))) {
1058 js = g_strdup (line);
1060 gchar* newjs = g_strconcat (js, line, NULL);
1067 if (uzbl.state.verbose)
1068 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1070 if (argv_idx (argv, 1)) {
1071 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1075 eval_js (web_view, js, result);
1077 g_array_free (lines, TRUE);
1082 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1083 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1084 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1085 webkit_web_view_unmark_text_matches (page);
1086 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1087 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1091 if (uzbl.state.searchtx) {
1092 if (uzbl.state.verbose)
1093 printf ("Searching: %s\n", uzbl.state.searchtx);
1094 webkit_web_view_set_highlight_text_matches (page, TRUE);
1095 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1100 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1102 search_text(page, argv, TRUE);
1106 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1108 search_text(page, argv, FALSE);
1112 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1113 (void) argv; (void) result;
1114 webkit_web_view_set_highlight_text_matches (page, FALSE);
1119 new_window_load_uri (const gchar * uri) {
1120 if (uzbl.behave.new_window) {
1121 GString *s = g_string_new ("");
1122 g_string_printf(s, "'%s'", uri);
1123 run_handler(uzbl.behave.new_window, s->str);
1126 GString* to_execute = g_string_new ("");
1127 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1129 for (i = 0; entries[i].long_name != NULL; i++) {
1130 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1131 gchar** str = (gchar**)entries[i].arg_data;
1133 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1137 if (uzbl.state.verbose)
1138 printf("\n%s\n", to_execute->str);
1139 g_spawn_command_line_async (to_execute->str, NULL);
1140 g_string_free (to_execute, TRUE);
1144 chain (WebKitWebView *page, GArray *argv, GString *result) {
1145 (void) page; (void) result;
1147 gchar **parts = NULL;
1149 while ((a = argv_idx(argv, i++))) {
1150 parts = g_strsplit (a, " ", 2);
1152 parse_command(parts[0], parts[1], result);
1158 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1162 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1168 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1172 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1178 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1183 int len = strlen(uzbl.state.keycmd);
1184 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1186 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1191 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1198 /* --Statusbar functions-- */
1200 build_progressbar_ascii(int percent) {
1201 int width=uzbl.gui.sbar.progress_w;
1204 GString *bar = g_string_new("");
1206 l = (double)percent*((double)width/100.);
1207 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1209 for(i=0; i<(int)l; i++)
1210 g_string_append(bar, uzbl.gui.sbar.progress_s);
1213 g_string_append(bar, uzbl.gui.sbar.progress_u);
1215 return g_string_free(bar, FALSE);
1217 /* --End Statusbar functions-- */
1220 sharg_append(GArray *a, const gchar *str) {
1221 const gchar *s = (str ? str : "");
1222 g_array_append_val(a, s);
1225 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1227 run_command (const gchar *command, const guint npre, const gchar **args,
1228 const gboolean sync, char **output_stdout) {
1229 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1232 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1233 gchar *pid = itos(getpid());
1234 gchar *xwin = itos(uzbl.xwin);
1236 sharg_append(a, command);
1237 for (i = 0; i < npre; i++) /* add n args before the default vars */
1238 sharg_append(a, args[i]);
1239 sharg_append(a, uzbl.state.config_file);
1240 sharg_append(a, pid);
1241 sharg_append(a, xwin);
1242 sharg_append(a, uzbl.comm.fifo_path);
1243 sharg_append(a, uzbl.comm.socket_path);
1244 sharg_append(a, uzbl.state.uri);
1245 sharg_append(a, uzbl.gui.main_title);
1247 for (i = npre; i < g_strv_length((gchar**)args); i++)
1248 sharg_append(a, args[i]);
1252 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1254 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1255 NULL, NULL, output_stdout, NULL, NULL, &err);
1256 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1257 NULL, NULL, NULL, &err);
1259 if (uzbl.state.verbose) {
1260 GString *s = g_string_new("spawned:");
1261 for (i = 0; i < (a->len); i++) {
1262 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1263 g_string_append_printf(s, " %s", qarg);
1266 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1267 printf("%s\n", s->str);
1268 g_string_free(s, TRUE);
1270 printf("Stdout: %s\n", *output_stdout);
1274 g_printerr("error on run_command: %s\n", err->message);
1279 g_array_free (a, TRUE);
1284 split_quoted(const gchar* src, const gboolean unquote) {
1285 /* split on unquoted space, return array of strings;
1286 remove a layer of quotes and backslashes if unquote */
1287 if (!src) return NULL;
1289 gboolean dq = FALSE;
1290 gboolean sq = FALSE;
1291 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1292 GString *s = g_string_new ("");
1296 for (p = src; *p != '\0'; p++) {
1297 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1298 else if (*p == '\\') { g_string_append_c(s, *p++);
1299 g_string_append_c(s, *p); }
1300 else if ((*p == '"') && unquote && !sq) dq = !dq;
1301 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1303 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1304 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1306 else if ((*p == ' ') && !dq && !sq) {
1307 dup = g_strdup(s->str);
1308 g_array_append_val(a, dup);
1309 g_string_truncate(s, 0);
1310 } else g_string_append_c(s, *p);
1312 dup = g_strdup(s->str);
1313 g_array_append_val(a, dup);
1314 ret = (gchar**)a->data;
1315 g_array_free (a, FALSE);
1316 g_string_free (s, TRUE);
1321 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1322 (void)web_view; (void)result;
1323 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1324 if (argv_idx(argv, 0))
1325 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1329 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1330 (void)web_view; (void)result;
1332 if (argv_idx(argv, 0))
1333 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1334 TRUE, &uzbl.comm.sync_stdout);
1338 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1339 (void)web_view; (void)result;
1340 if (!uzbl.behave.shell_cmd) {
1341 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1346 gchar *spacer = g_strdup("");
1347 g_array_insert_val(argv, 1, spacer);
1348 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1350 for (i = 1; i < g_strv_length(cmd); i++)
1351 g_array_prepend_val(argv, cmd[i]);
1353 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1359 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1360 (void)web_view; (void)result;
1361 if (!uzbl.behave.shell_cmd) {
1362 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1367 gchar *spacer = g_strdup("");
1368 g_array_insert_val(argv, 1, spacer);
1369 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1371 for (i = 1; i < g_strv_length(cmd); i++)
1372 g_array_prepend_val(argv, cmd[i]);
1374 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1375 TRUE, &uzbl.comm.sync_stdout);
1381 talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
1382 (void)web_view; (void)result;
1385 struct sockaddr_un sa;
1392 if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1394 /* This function could be optimised by storing a hash table of socket paths
1395 and associated connected file descriptors rather than closing and
1396 re-opening for every call. Also we could launch a script if socket connect
1399 /* First element argv[0] is path to socket. Following elements are tokens to
1400 write to the socket. We write them as a single packet with each token
1401 separated by an ASCII nul (\0). */
1403 g_printerr("talk_to_socket called with only %d args (need at least two).\n",
1408 /* copy socket path, null terminate result */
1409 sockpath = g_array_index(argv, char*, 0);
1410 g_strlcpy(sa.sun_path, sockpath, sizeof(sa.sun_path));
1411 sa.sun_family = AF_UNIX;
1413 /* create socket file descriptor and connect it to path */
1414 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
1416 g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
1419 if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
1420 g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
1425 /* build request vector */
1426 iov = g_malloc(sizeof(struct iovec) * (argv->len - 1));
1428 g_printerr("talk_to_socket: unable to allocated memory for token vector\n");
1432 for(i = 1; i < argv->len; ++i) {
1433 iov[i - 1].iov_base = g_array_index(argv, char*, i);
1434 iov[i - 1].iov_len = strlen(iov[i - 1].iov_base) + 1; /* string plus \0 */
1438 ret = writev(fd, iov, argv->len - 1);
1441 g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
1446 /* wait for a response, with a 500ms timeout */
1448 pfd.events = POLLIN;
1450 ret = poll(&pfd, 1, 500);
1452 if(ret == 0) errno = ETIMEDOUT;
1453 if(errno == EINTR) continue;
1454 g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
1460 /* get length of response */
1461 if(ioctl(fd, FIONREAD, &len) == -1) {
1462 g_printerr("talk_to_socket: cannot find daemon response length, "
1463 "ioctl failed (%s)\n", strerror(errno));
1468 /* if there is a response, read it */
1470 uzbl.comm.sync_stdout = g_malloc(len + 1);
1471 if(!uzbl.comm.sync_stdout) {
1472 g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
1476 uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
1478 ret = read(fd, uzbl.comm.sync_stdout, len);
1480 g_printerr("talk_to_socket: failed to read from socket (%s)\n",
1493 parse_command(const char *cmd, const char *param, GString *result) {
1496 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1498 gchar **par = split_quoted(param, TRUE);
1499 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1501 if (c->no_split) { /* don't split */
1502 sharg_append(a, param);
1504 for (i = 0; i < g_strv_length(par); i++)
1505 sharg_append(a, par[i]);
1508 if (result == NULL) {
1509 GString *result_print = g_string_new("");
1511 c->function(uzbl.gui.web_view, a, result_print);
1512 if (result_print->len)
1513 printf("%*s\n", (int)result_print->len, result_print->str);
1515 g_string_free(result_print, TRUE);
1517 c->function(uzbl.gui.web_view, a, result);
1520 g_array_free (a, TRUE);
1523 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1530 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
1531 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1532 (GType) SOUP_SESSION_PROXY_URI);
1535 suri = soup_uri_new(uzbl.net.proxy_url);
1536 g_object_set(G_OBJECT(uzbl.net.soup_session),
1537 SOUP_SESSION_PROXY_URI,
1539 soup_uri_free(suri);
1546 if(file_exists(uzbl.gui.icon)) {
1547 if (uzbl.gui.main_window)
1548 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1550 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1556 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1557 g_array_append_val (a, uzbl.state.uri);
1558 load_uri(uzbl.gui.web_view, a, NULL);
1559 g_array_free (a, TRUE);
1563 cmd_always_insert_mode() {
1564 set_insert_mode(uzbl.behave.always_insert_mode);
1570 g_object_set(G_OBJECT(uzbl.net.soup_session),
1571 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1575 cmd_max_conns_host() {
1576 g_object_set(G_OBJECT(uzbl.net.soup_session),
1577 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1582 soup_session_remove_feature
1583 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1584 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1585 /*g_free(uzbl.net.soup_logger);*/
1587 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1588 soup_session_add_feature(uzbl.net.soup_session,
1589 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1594 return webkit_web_view_get_settings(uzbl.gui.web_view);
1599 WebKitWebSettings *ws = view_settings();
1600 if (uzbl.behave.font_size > 0) {
1601 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1604 if (uzbl.behave.monospace_size > 0) {
1605 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1606 uzbl.behave.monospace_size, NULL);
1608 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1609 uzbl.behave.font_size, NULL);
1614 cmd_default_font_family() {
1615 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1616 uzbl.behave.default_font_family, NULL);
1620 cmd_monospace_font_family() {
1621 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1622 uzbl.behave.monospace_font_family, NULL);
1626 cmd_sans_serif_font_family() {
1627 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1628 uzbl.behave.sans_serif_font_family, NULL);
1632 cmd_serif_font_family() {
1633 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1634 uzbl.behave.serif_font_family, NULL);
1638 cmd_cursive_font_family() {
1639 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1640 uzbl.behave.cursive_font_family, NULL);
1644 cmd_fantasy_font_family() {
1645 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1646 uzbl.behave.fantasy_font_family, NULL);
1651 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1655 cmd_disable_plugins() {
1656 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1657 !uzbl.behave.disable_plugins, NULL);
1661 cmd_disable_scripts() {
1662 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1663 !uzbl.behave.disable_scripts, NULL);
1667 cmd_minimum_font_size() {
1668 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1669 uzbl.behave.minimum_font_size, NULL);
1672 cmd_autoload_img() {
1673 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1674 uzbl.behave.autoload_img, NULL);
1679 cmd_autoshrink_img() {
1680 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1681 uzbl.behave.autoshrink_img, NULL);
1686 cmd_enable_spellcheck() {
1687 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1688 uzbl.behave.enable_spellcheck, NULL);
1692 cmd_enable_private() {
1693 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1694 uzbl.behave.enable_private, NULL);
1699 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1700 uzbl.behave.print_bg, NULL);
1705 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1706 uzbl.behave.style_uri, NULL);
1710 cmd_resizable_txt() {
1711 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1712 uzbl.behave.resizable_txt, NULL);
1716 cmd_default_encoding() {
1717 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1718 uzbl.behave.default_encoding, NULL);
1722 cmd_enforce_96dpi() {
1723 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1724 uzbl.behave.enforce_96dpi, NULL);
1728 cmd_caret_browsing() {
1729 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1730 uzbl.behave.caret_browsing, NULL);
1734 cmd_cookie_handler() {
1735 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1736 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1737 if ((g_strcmp0(split[0], "sh") == 0) ||
1738 (g_strcmp0(split[0], "spawn") == 0)) {
1739 g_free (uzbl.behave.cookie_handler);
1740 uzbl.behave.cookie_handler =
1741 g_strdup_printf("sync_%s %s", split[0], split[1]);
1748 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1749 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1750 if ((g_strcmp0(split[0], "sh") == 0) ||
1751 (g_strcmp0(split[0], "spawn") == 0)) {
1752 g_free (uzbl.behave.new_window);
1753 uzbl.behave.new_window =
1754 g_strdup_printf("%s %s", split[0], split[1]);
1761 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1766 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1771 if(uzbl.behave.inject_html) {
1772 webkit_web_view_load_html_string (uzbl.gui.web_view,
1773 uzbl.behave.inject_html, NULL);
1782 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1783 uzbl.behave.modmask = 0;
1785 if(uzbl.behave.modkey)
1786 g_free(uzbl.behave.modkey);
1787 uzbl.behave.modkey = buf;
1789 for (i = 0; modkeys[i].key != NULL; i++) {
1790 if (g_strrstr(buf, modkeys[i].key))
1791 uzbl.behave.modmask |= modkeys[i].mask;
1797 if (*uzbl.net.useragent == ' ') {
1798 g_free (uzbl.net.useragent);
1799 uzbl.net.useragent = NULL;
1801 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1802 uzbl.net.useragent, NULL);
1808 if (!uzbl.gui.scrolled_win &&
1812 gtk_widget_ref(uzbl.gui.scrolled_win);
1813 gtk_widget_ref(uzbl.gui.mainbar);
1814 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1815 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1817 if(uzbl.behave.status_top) {
1818 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1819 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1822 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.mainbar, FALSE, TRUE, 0);
1825 gtk_widget_unref(uzbl.gui.scrolled_win);
1826 gtk_widget_unref(uzbl.gui.mainbar);
1827 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1832 set_var_value(const gchar *name, gchar *val) {
1833 uzbl_cmdprop *c = NULL;
1836 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1838 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1839 if(!c->writeable) return FALSE;
1841 /* check for the variable type */
1842 if (c->type == TYPE_STR) {
1843 buf = expand(val, 0);
1846 } else if(c->type == TYPE_INT) {
1847 buf = expand(val, 0);
1848 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1850 } else if (c->type == TYPE_FLOAT) {
1851 buf = expand(val, 0);
1852 *c->ptr.f = strtod(buf, &endp);
1856 /* invoke a command specific function */
1857 if(c->func) c->func();
1859 /* check wether name violates our naming scheme */
1860 if(strpbrk(name, invalid_chars)) {
1861 if (uzbl.state.verbose)
1862 printf("Invalid variable name\n");
1867 c = malloc(sizeof(uzbl_cmdprop));
1872 buf = expand(val, 0);
1873 c->ptr.s = malloc(sizeof(char *));
1875 g_hash_table_insert(uzbl.comm.proto_var,
1876 g_strdup(name), (gpointer) c);
1881 enum {M_CMD, M_HTML};
1883 parse_cmd_line(const char *ctl_line, GString *result) {
1886 if((ctl_line[0] == '#') /* Comments */
1887 || (ctl_line[0] == ' ')
1888 || (ctl_line[0] == '\n'))
1889 ; /* ignore these lines */
1890 else { /* parse a command */
1892 gchar **tokens = NULL;
1893 len = strlen(ctl_line);
1895 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1896 ctlstrip = g_strndup(ctl_line, len - 1);
1897 else ctlstrip = g_strdup(ctl_line);
1899 tokens = g_strsplit(ctlstrip, " ", 2);
1900 parse_command(tokens[0], tokens[1], result);
1907 build_stream_name(int type, const gchar* dir) {
1908 State *s = &uzbl.state;
1912 str = g_strdup_printf
1913 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1914 } else if (type == SOCKET) {
1915 str = g_strdup_printf
1916 ("%s/uzbl_socket_%s", dir, s->instance_name);
1922 control_fifo(GIOChannel *gio, GIOCondition condition) {
1923 if (uzbl.state.verbose)
1924 printf("triggered\n");
1929 if (condition & G_IO_HUP)
1930 g_error ("Fifo: Read end of pipe died!\n");
1933 g_error ("Fifo: GIOChannel broke\n");
1935 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1936 if (ret == G_IO_STATUS_ERROR) {
1937 g_error ("Fifo: Error reading: %s\n", err->message);
1941 parse_cmd_line(ctl_line, NULL);
1948 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1949 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1950 if (unlink(uzbl.comm.fifo_path) == -1)
1951 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1952 g_free(uzbl.comm.fifo_path);
1953 uzbl.comm.fifo_path = NULL;
1956 GIOChannel *chan = NULL;
1957 GError *error = NULL;
1958 gchar *path = build_stream_name(FIFO, dir);
1960 if (!file_exists(path)) {
1961 if (mkfifo (path, 0666) == 0) {
1962 // 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.
1963 chan = g_io_channel_new_file(path, "r+", &error);
1965 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1966 if (uzbl.state.verbose)
1967 printf ("init_fifo: created successfully as %s\n", path);
1968 uzbl.comm.fifo_path = path;
1970 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1971 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1972 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1973 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1975 /* if we got this far, there was an error; cleanup */
1976 if (error) g_error_free (error);
1983 control_stdin(GIOChannel *gio, GIOCondition condition) {
1985 gchar *ctl_line = NULL;
1988 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1989 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1992 parse_cmd_line(ctl_line, NULL);
2000 GIOChannel *chan = NULL;
2001 GError *error = NULL;
2003 chan = g_io_channel_unix_new(fileno(stdin));
2005 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2006 g_error ("Stdin: could not add watch\n");
2008 if (uzbl.state.verbose)
2009 printf ("Stdin: watch added successfully\n");
2012 g_error ("Stdin: Error while opening: %s\n", error->message);
2014 if (error) g_error_free (error);
2018 control_socket(GIOChannel *chan) {
2019 struct sockaddr_un remote;
2020 unsigned int t = sizeof(remote);
2022 GIOChannel *clientchan;
2024 clientsock = accept (g_io_channel_unix_get_fd(chan),
2025 (struct sockaddr *) &remote, &t);
2027 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2028 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2029 (GIOFunc) control_client_socket, clientchan);
2036 control_client_socket(GIOChannel *clientchan) {
2038 GString *result = g_string_new("");
2039 GError *error = NULL;
2043 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2044 if (ret == G_IO_STATUS_ERROR) {
2045 g_warning ("Error reading: %s\n", error->message);
2046 g_io_channel_shutdown(clientchan, TRUE, &error);
2048 } else if (ret == G_IO_STATUS_EOF) {
2049 /* shutdown and remove channel watch from main loop */
2050 g_io_channel_shutdown(clientchan, TRUE, &error);
2055 parse_cmd_line (ctl_line, result);
2056 g_string_append_c(result, '\n');
2057 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2059 if (ret == G_IO_STATUS_ERROR) {
2060 g_warning ("Error writing: %s", error->message);
2062 g_io_channel_flush(clientchan, &error);
2065 if (error) g_error_free (error);
2066 g_string_free(result, TRUE);
2072 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2073 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2074 if (unlink(uzbl.comm.socket_path) == -1)
2075 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2076 g_free(uzbl.comm.socket_path);
2077 uzbl.comm.socket_path = NULL;
2085 GIOChannel *chan = NULL;
2087 struct sockaddr_un local;
2088 gchar *path = build_stream_name(SOCKET, dir);
2090 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2092 local.sun_family = AF_UNIX;
2093 strcpy (local.sun_path, path);
2094 unlink (local.sun_path);
2096 len = strlen (local.sun_path) + sizeof (local.sun_family);
2097 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2098 if (uzbl.state.verbose)
2099 printf ("init_socket: opened in %s\n", path);
2102 if( (chan = g_io_channel_unix_new(sock)) ) {
2103 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2104 uzbl.comm.socket_path = path;
2107 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2109 /* if we got this far, there was an error; cleanup */
2116 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2117 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2119 // this function may be called very early when the templates are not set (yet), hence the checks
2121 update_title (void) {
2122 Behaviour *b = &uzbl.behave;
2125 if (b->show_status) {
2126 if (b->title_format_short) {
2127 parsed = expand(b->title_format_short, 0);
2128 if (uzbl.gui.main_window)
2129 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2132 if (b->status_format) {
2133 parsed = expand(b->status_format, 0);
2134 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2137 if (b->status_background) {
2139 gdk_color_parse (b->status_background, &color);
2140 //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)
2141 if (uzbl.gui.main_window)
2142 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2143 else if (uzbl.gui.plug)
2144 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2147 if (b->title_format_long) {
2148 parsed = expand(b->title_format_long, 0);
2149 if (uzbl.gui.main_window)
2150 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2157 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2161 retrieve_geometry();
2166 key_press_cb (GtkWidget* window, GdkEventKey* event)
2168 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2172 if (event->type != GDK_KEY_PRESS ||
2173 event->keyval == GDK_Page_Up ||
2174 event->keyval == GDK_Page_Down ||
2175 event->keyval == GDK_Up ||
2176 event->keyval == GDK_Down ||
2177 event->keyval == GDK_Left ||
2178 event->keyval == GDK_Right ||
2179 event->keyval == GDK_Shift_L ||
2180 event->keyval == GDK_Shift_R)
2183 /* turn off insert mode (if always_insert_mode is not used) */
2184 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2185 set_insert_mode(uzbl.behave.always_insert_mode);
2190 if (uzbl.behave.insert_mode &&
2191 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2192 (!uzbl.behave.modmask)
2197 if (event->keyval == GDK_Escape) {
2200 dehilight(uzbl.gui.web_view, NULL, NULL);
2204 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2205 if (event->keyval == GDK_Insert) {
2207 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2208 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2210 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2213 GString* keycmd = g_string_new(uzbl.state.keycmd);
2214 g_string_append (keycmd, str);
2215 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2222 if (event->keyval == GDK_BackSpace)
2223 keycmd_bs(NULL, NULL, NULL);
2225 gboolean key_ret = FALSE;
2226 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2229 GString* keycmd = g_string_new(uzbl.state.keycmd);
2230 g_string_append(keycmd, event->string);
2231 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2234 run_keycmd(key_ret);
2236 if (key_ret) return (!uzbl.behave.insert_mode);
2241 run_keycmd(const gboolean key_ret) {
2242 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2244 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2246 parse_command(act->name, act->param, NULL);
2250 /* try if it's an incremental keycmd or one that takes args, and run it */
2251 GString* short_keys = g_string_new ("");
2252 GString* short_keys_inc = g_string_new ("");
2254 guint len = strlen(uzbl.state.keycmd);
2255 for (i=0; i<len; i++) {
2256 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2257 g_string_assign(short_keys_inc, short_keys->str);
2258 g_string_append_c(short_keys, '_');
2259 g_string_append_c(short_keys_inc, '*');
2261 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2262 /* run normal cmds only if return was pressed */
2263 exec_paramcmd(act, i);
2266 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2267 if (key_ret) /* just quit the incremental command on return */
2269 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2273 g_string_truncate(short_keys, short_keys->len - 1);
2275 g_string_free (short_keys, TRUE);
2276 g_string_free (short_keys_inc, TRUE);
2280 exec_paramcmd(const Action *act, const guint i) {
2281 GString *parampart = g_string_new (uzbl.state.keycmd);
2282 GString *actionname = g_string_new ("");
2283 GString *actionparam = g_string_new ("");
2284 g_string_erase (parampart, 0, i+1);
2286 g_string_printf (actionname, act->name, parampart->str);
2288 g_string_printf (actionparam, act->param, parampart->str);
2289 parse_command(actionname->str, actionparam->str, NULL);
2290 g_string_free(actionname, TRUE);
2291 g_string_free(actionparam, TRUE);
2292 g_string_free(parampart, TRUE);
2300 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2302 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2303 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2304 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2305 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2306 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2307 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2308 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2309 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2310 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2311 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2318 g->mainbar = gtk_hbox_new (FALSE, 0);
2320 /* keep a reference to the bar so we can re-pack it at runtime*/
2321 //sbar_ref = g_object_ref(g->mainbar);
2323 g->mainbar_label = gtk_label_new ("");
2324 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2325 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2326 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2327 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2328 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2329 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2335 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2336 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2337 gtk_widget_set_name (window, "Uzbl browser");
2338 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2339 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2340 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2347 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2348 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2349 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2356 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2358 If actname is one that calls an external command, this function will inject
2359 newargs in front of the user-provided args in that command line. They will
2360 come become after the body of the script (in sh) or after the name of
2361 the command to execute (in spawn).
2362 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2363 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2365 The return value consist of two strings: the action (sh, ...) and its args.
2367 If act is not one that calls an external command, then the given action merely
2370 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2371 /* Arrr! Here be memory leaks */
2372 gchar *actdup = g_strdup(actname);
2373 g_array_append_val(rets, actdup);
2375 if ((g_strcmp0(actname, "spawn") == 0) ||
2376 (g_strcmp0(actname, "sh") == 0) ||
2377 (g_strcmp0(actname, "sync_spawn") == 0) ||
2378 (g_strcmp0(actname, "sync_sh") == 0) ||
2379 (g_strcmp0(actname, "talk_to_socket") == 0)) {
2381 GString *a = g_string_new("");
2382 gchar **spawnparts = split_quoted(origargs, FALSE);
2383 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2384 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2386 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2387 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2389 g_array_append_val(rets, a->str);
2390 g_string_free(a, FALSE);
2391 g_strfreev(spawnparts);
2393 gchar *origdup = g_strdup(origargs);
2394 g_array_append_val(rets, origdup);
2396 return (gchar**)g_array_free(rets, FALSE);
2400 run_handler (const gchar *act, const gchar *args) {
2401 /* Consider this code a temporary hack to make the handlers usable.
2402 In practice, all this splicing, injection, and reconstruction is
2403 inefficient, annoying and hard to manage. Potential pitfalls arise
2404 when the handler specific args 1) are not quoted (the handler
2405 callbacks should take care of this) 2) are quoted but interfere
2406 with the users' own quotation. A more ideal solution is
2407 to refactor parse_command so that it doesn't just take a string
2408 and execute it; rather than that, we should have a function which
2409 returns the argument vector parsed from the string. This vector
2410 could be modified (e.g. insert additional args into it) before
2411 passing it to the next function that actually executes it. Though
2412 it still isn't perfect for chain actions.. will reconsider & re-
2413 factor when I have the time. -duc */
2415 char **parts = g_strsplit(act, " ", 2);
2417 if (g_strcmp0(parts[0], "chain") == 0) {
2418 GString *newargs = g_string_new("");
2419 gchar **chainparts = split_quoted(parts[1], FALSE);
2421 /* for every argument in the chain, inject the handler args
2422 and make sure the new parts are wrapped in quotes */
2423 gchar **cp = chainparts;
2425 gchar *quotless = NULL;
2426 gchar **spliced_quotless = NULL; // sigh -_-;
2427 gchar **inpart = NULL;
2430 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2432 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2433 } else quotless = g_strdup(*cp);
2435 spliced_quotless = g_strsplit(quotless, " ", 2);
2436 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2437 g_strfreev(spliced_quotless);
2439 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2445 parse_command(parts[0], &(newargs->str[1]), NULL);
2446 g_string_free(newargs, TRUE);
2447 g_strfreev(chainparts);
2450 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2451 parse_command(inparts[0], inparts[1], NULL);
2459 add_binding (const gchar *key, const gchar *act) {
2460 char **parts = g_strsplit(act, " ", 2);
2467 if (uzbl.state.verbose)
2468 printf ("Binding %-10s : %s\n", key, act);
2469 action = new_action(parts[0], parts[1]);
2471 if (g_hash_table_remove (uzbl.bindings, key))
2472 g_warning ("Overwriting existing binding for \"%s\"", key);
2473 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2478 get_xdg_var (XDG_Var xdg) {
2479 const gchar* actual_value = getenv (xdg.environmental);
2480 const gchar* home = getenv ("HOME");
2481 gchar* return_value;
2483 if (! actual_value || strcmp (actual_value, "") == 0) {
2484 if (xdg.default_value) {
2485 return_value = str_replace ("~", home, xdg.default_value);
2487 return_value = NULL;
2490 return_value = str_replace("~", home, actual_value);
2493 return return_value;
2497 find_xdg_file (int xdg_type, const char* filename) {
2498 /* xdg_type = 0 => config
2499 xdg_type = 1 => data
2500 xdg_type = 2 => cache*/
2502 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2503 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2506 gchar* temporary_string;
2510 if (! file_exists (temporary_file) && xdg_type != 2) {
2511 buf = get_xdg_var (XDG[3 + xdg_type]);
2512 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2515 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2516 g_free (temporary_file);
2517 temporary_file = g_strconcat (temporary_string, filename, NULL);
2521 //g_free (temporary_string); - segfaults.
2523 if (file_exists (temporary_file)) {
2524 return temporary_file;
2526 g_free(temporary_file);
2532 State *s = &uzbl.state;
2533 Network *n = &uzbl.net;
2535 for (i = 0; default_config[i].command != NULL; i++) {
2536 parse_cmd_line(default_config[i].command, NULL);
2539 if (g_strcmp0(s->config_file, "-") == 0) {
2540 s->config_file = NULL;
2544 else if (!s->config_file) {
2545 s->config_file = find_xdg_file (0, "/uzbl/config");
2548 if (s->config_file) {
2549 GArray* lines = read_file_by_line (s->config_file);
2553 while ((line = g_array_index(lines, gchar*, i))) {
2554 parse_cmd_line (line, NULL);
2558 g_array_free (lines, TRUE);
2560 if (uzbl.state.verbose)
2561 printf ("No configuration file loaded.\n");
2564 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2567 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2570 if (!uzbl.behave.cookie_handler)
2573 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2574 GString *s = g_string_new ("");
2575 SoupURI * soup_uri = soup_message_get_uri(msg);
2576 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2577 run_handler(uzbl.behave.cookie_handler, s->str);
2579 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2580 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2581 if ( p != NULL ) *p = '\0';
2582 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2584 if (uzbl.comm.sync_stdout)
2585 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2587 g_string_free(s, TRUE);
2591 save_cookies (SoupMessage *msg, gpointer user_data){
2595 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2596 cookie = soup_cookie_to_set_cookie_header(ck->data);
2597 SoupURI * soup_uri = soup_message_get_uri(msg);
2598 GString *s = g_string_new ("");
2599 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2600 run_handler(uzbl.behave.cookie_handler, s->str);
2602 g_string_free(s, TRUE);
2607 /* --- WEBINSPECTOR --- */
2609 hide_window_cb(GtkWidget *widget, gpointer data) {
2612 gtk_widget_hide(widget);
2616 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2619 (void) web_inspector;
2620 GtkWidget* scrolled_window;
2621 GtkWidget* new_web_view;
2624 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2625 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2626 G_CALLBACK(hide_window_cb), NULL);
2628 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2629 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2630 gtk_widget_show(g->inspector_window);
2632 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2633 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2634 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2635 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2636 gtk_widget_show(scrolled_window);
2638 new_web_view = webkit_web_view_new();
2639 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2641 return WEBKIT_WEB_VIEW(new_web_view);
2645 inspector_show_window_cb (WebKitWebInspector* inspector){
2647 gtk_widget_show(uzbl.gui.inspector_window);
2651 /* TODO: Add variables and code to make use of these functions */
2653 inspector_close_window_cb (WebKitWebInspector* inspector){
2659 inspector_attach_window_cb (WebKitWebInspector* inspector){
2665 inspector_detach_window_cb (WebKitWebInspector* inspector){
2671 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2677 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2683 set_up_inspector() {
2685 WebKitWebSettings *settings = view_settings();
2686 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2688 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2689 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2690 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2691 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2692 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2693 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2694 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2696 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2700 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2702 uzbl_cmdprop *c = v;
2707 if(c->type == TYPE_STR)
2708 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2709 else if(c->type == TYPE_INT)
2710 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2711 else if(c->type == TYPE_FLOAT)
2712 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2716 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2720 printf("bind %s = %s %s\n", (char *)k ,
2721 (char *)a->name, a->param?(char *)a->param:"");
2726 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2727 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2731 retrieve_geometry() {
2733 GString *buf = g_string_new("");
2735 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2736 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2738 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2740 if(uzbl.gui.geometry)
2741 g_free(uzbl.gui.geometry);
2742 uzbl.gui.geometry = g_string_free(buf, FALSE);
2745 /* set up gtk, gobject, variable defaults and other things that tests and other
2746 * external applications need to do anyhow */
2748 initialize(int argc, char *argv[]) {
2749 if (!g_thread_supported ())
2750 g_thread_init (NULL);
2751 uzbl.state.executable_path = g_strdup(argv[0]);
2752 uzbl.state.selected_url = NULL;
2753 uzbl.state.searchtx = NULL;
2755 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2756 g_option_context_add_main_entries (context, entries, NULL);
2757 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2758 g_option_context_parse (context, &argc, &argv, NULL);
2759 g_option_context_free(context);
2761 if (uzbl.behave.print_version) {
2762 printf("Commit: %s\n", COMMIT);
2766 /* initialize hash table */
2767 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2769 uzbl.net.soup_session = webkit_get_default_session();
2770 uzbl.state.keycmd = g_strdup("");
2772 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2773 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2774 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2775 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2777 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2778 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2779 uzbl.gui.sbar.progress_w = 10;
2781 /* default mode indicators */
2782 uzbl.behave.insert_indicator = g_strdup("I");
2783 uzbl.behave.cmd_indicator = g_strdup("C");
2785 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2786 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2787 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2788 uzbl.info.arch = ARCH;
2789 uzbl.info.commit = COMMIT;
2792 make_var_to_name_hash();
2797 #ifndef UZBL_LIBRARY
2800 main (int argc, char* argv[]) {
2801 initialize(argc, argv);
2803 gtk_init (&argc, &argv);
2805 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2806 //main_window_ref = g_object_ref(scrolled_window);
2807 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2808 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2810 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2811 GTK_WIDGET (uzbl.gui.web_view));
2813 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2817 /* initial packing */
2818 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2819 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2821 if (uzbl.state.socket_id) {
2822 uzbl.gui.plug = create_plug ();
2823 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2824 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2826 uzbl.gui.main_window = create_window ();
2827 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2828 gtk_widget_show_all (uzbl.gui.main_window);
2829 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2832 if(!uzbl.state.instance_name)
2833 uzbl.state.instance_name = itos((int)uzbl.xwin);
2835 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2837 if (uzbl.state.verbose) {
2838 printf("Uzbl start location: %s\n", argv[0]);
2839 if (uzbl.state.socket_id)
2840 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2842 printf("window_id %i\n",(int) uzbl.xwin);
2843 printf("pid %i\n", getpid ());
2844 printf("name: %s\n", uzbl.state.instance_name);
2847 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2848 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2849 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2850 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2851 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2853 /* Check uzbl is in window mode before getting/setting geometry */
2854 if (uzbl.gui.main_window) {
2855 if(uzbl.gui.geometry)
2858 retrieve_geometry();
2861 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2862 if (argc > 1 && !uzbl.state.uri)
2863 uri_override = g_strdup(argv[1]);
2864 gboolean verbose_override = uzbl.state.verbose;
2868 if (!uzbl.behave.always_insert_mode)
2869 set_insert_mode(FALSE);
2871 if (!uzbl.behave.show_status)
2872 gtk_widget_hide(uzbl.gui.mainbar);
2879 if (verbose_override > uzbl.state.verbose)
2880 uzbl.state.verbose = verbose_override;
2883 set_var_value("uri", uri_override);
2884 g_free(uri_override);
2885 } else if (uzbl.state.uri)
2891 return EXIT_SUCCESS;
2895 /* vi: set et ts=4: */