1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
63 /* commandline arguments (set initial values for the state variables) */
65 GOptionEntry entries[] =
67 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
68 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
69 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
70 "Whether to print all messages or just errors.", NULL },
71 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
72 "Name of the current instance (defaults to Xorg window id)", "NAME" },
73 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
74 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
75 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
76 "Socket ID", "SOCKET" },
77 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
78 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
79 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
80 "Print the version and exit", NULL },
81 { NULL, 0, 0, 0, NULL, NULL, NULL }
84 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
86 /* associate command names to their properties */
87 typedef const struct {
88 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
89 the PTR() macro is kind of preventing this change at the moment. */
94 /*@null@*/ void (*func)(void);
97 /* abbreviations to help keep the table's width humane */
98 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
99 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
104 } var_name_to_ptr[] = {
105 /* variable name pointer to variable in code type dump callback function */
106 /* ---------------------------------------------------------------------------------------------- */
107 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
108 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
109 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
110 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
111 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
112 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
113 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
114 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
115 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
116 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
117 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
118 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
119 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
120 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
121 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
122 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
123 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
124 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
125 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
126 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
127 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
128 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
129 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
130 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
131 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
132 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
133 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
134 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
135 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
136 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
137 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
138 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
139 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
140 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
141 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
142 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
143 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
144 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
145 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
146 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
148 /* exported WebKitWebSettings properties */
149 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
150 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
151 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
152 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
153 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
154 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
155 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
156 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
157 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
158 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
159 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
160 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
161 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
162 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
163 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
164 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
166 /* constants (not dumpable or writeable) */
167 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
168 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
169 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
170 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
171 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
172 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
173 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
174 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
175 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
176 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
177 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
179 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
180 }, *n2v_p = var_name_to_ptr;
184 /*@null@*/ char *key;
187 { "SHIFT", GDK_SHIFT_MASK }, // shift
188 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
189 { "CONTROL", GDK_CONTROL_MASK }, // control
190 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
191 { "MOD2", GDK_MOD2_MASK }, // 5th mod
192 { "MOD3", GDK_MOD3_MASK }, // 6th mod
193 { "MOD4", GDK_MOD4_MASK }, // 7th mod
194 { "MOD5", GDK_MOD5_MASK }, // 8th mod
195 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
196 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
197 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
198 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
199 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
200 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
201 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
202 { "META", GDK_META_MASK }, // meta (since 2.10)
207 /* construct a hash from the var_name_to_ptr array for quick access */
209 make_var_to_name_hash() {
210 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
212 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
217 /* --- UTILITY FUNCTIONS --- */
218 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
220 get_exp_type(const gchar *s) {
224 else if(*(s+1) == '{')
225 return EXP_BRACED_VAR;
226 else if(*(s+1) == '<')
228 else if(*(s+1) == '[')
231 return EXP_SIMPLE_VAR;
238 * recurse == 1: don't expand '@(command)@'
239 * recurse == 2: don't expand '@<java script>@'
242 expand(const char *s, guint recurse) {
245 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
249 gchar *cmd_stdout = NULL;
251 GString *buf = g_string_new("");
252 GString *js_ret = g_string_new("");
257 g_string_append_c(buf, *++s);
262 etype = get_exp_type(s);
267 vend = strpbrk(s, end_simple_var);
268 if(!vend) vend = strchr(s, '\0');
272 vend = strchr(s, '}');
273 if(!vend) vend = strchr(s, '\0');
277 vend = strstr(s, ")@");
278 if(!vend) vend = strchr(s, '\0');
282 vend = strstr(s, ">@");
283 if(!vend) vend = strchr(s, '\0');
287 vend = strstr(s, "]@");
288 if(!vend) vend = strchr(s, '\0');
293 ret = strndup(s, vend-s);
297 if(etype == EXP_SIMPLE_VAR ||
298 etype == EXP_BRACED_VAR) {
299 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
300 if(c->type == TYPE_STR && *c->ptr != NULL) {
301 g_string_append(buf, (gchar *)*c->ptr);
302 } else if(c->type == TYPE_INT) {
303 g_string_append_printf(buf, "%d", (int)*c->ptr);
305 else if(c->type == TYPE_FLOAT) {
306 g_string_append_printf(buf, "%f", *(float *)c->ptr);
310 if(etype == EXP_SIMPLE_VAR)
315 else if(recurse != 1 &&
317 mycmd = expand(ret, 1);
318 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
322 g_printerr("error on running command: %s\n", err->message);
325 else if (*cmd_stdout) {
326 size_t len = strlen(cmd_stdout);
328 if(len > 0 && cmd_stdout[len-1] == '\n')
329 cmd_stdout[--len] = '\0'; /* strip trailing newline */
331 g_string_append(buf, cmd_stdout);
336 else if(recurse != 2 &&
338 mycmd = expand(ret, 2);
339 eval_js(uzbl.gui.web_view, mycmd, js_ret);
343 g_string_append(buf, js_ret->str);
344 g_string_free(js_ret, TRUE);
345 js_ret = g_string_new("");
349 else if(etype == EXP_ESCAPE) {
350 mycmd = expand(ret, 0);
351 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
353 g_string_append(buf, escaped);
365 g_string_append_c(buf, *s);
370 g_string_free(js_ret, TRUE);
371 return g_string_free(buf, FALSE);
378 snprintf(tmp, sizeof(tmp), "%i", val);
379 return g_strdup(tmp);
383 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
386 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
389 str_replace (const char* search, const char* replace, const char* string) {
393 buf = g_strsplit (string, search, -1);
394 ret = g_strjoinv (replace, buf);
395 g_strfreev(buf); // somebody said this segfaults
401 read_file_by_line (gchar *path) {
402 GIOChannel *chan = NULL;
403 gchar *readbuf = NULL;
405 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
408 chan = g_io_channel_new_file(path, "r", NULL);
411 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
412 const gchar* val = g_strdup (readbuf);
413 g_array_append_val (lines, val);
418 g_io_channel_unref (chan);
420 fprintf(stderr, "File '%s' not be read.\n", path);
427 parseenv (char* string) {
428 extern char** environ;
429 gchar* tmpstr = NULL;
433 while (environ[i] != NULL) {
434 gchar** env = g_strsplit (environ[i], "=", 2);
435 gchar* envname = g_strconcat ("$", env[0], NULL);
437 if (g_strrstr (string, envname) != NULL) {
438 tmpstr = g_strdup(string);
440 string = str_replace(envname, env[1], tmpstr);
445 g_strfreev (env); // somebody said this breaks uzbl
453 setup_signal(int signr, sigfunc *shandler) {
454 struct sigaction nh, oh;
456 nh.sa_handler = shandler;
457 sigemptyset(&nh.sa_mask);
460 if(sigaction(signr, &nh, &oh) < 0)
468 if (uzbl.behave.fifo_dir)
469 unlink (uzbl.comm.fifo_path);
470 if (uzbl.behave.socket_dir)
471 unlink (uzbl.comm.socket_path);
473 g_free(uzbl.state.executable_path);
474 g_free(uzbl.state.keycmd);
475 g_hash_table_destroy(uzbl.bindings);
476 g_hash_table_destroy(uzbl.behave.commands);
479 /* used for html_mode_timeout
480 * be sure to extend this function to use
481 * more timers if needed in other places
484 set_timeout(int seconds) {
486 memset(&t, 0, sizeof t);
488 t.it_value.tv_sec = seconds;
489 t.it_value.tv_usec = 0;
490 setitimer(ITIMER_REAL, &t, NULL);
493 /* --- SIGNAL HANDLER --- */
496 catch_sigterm(int s) {
502 catch_sigint(int s) {
512 set_var_value("mode", "0");
517 /* --- CALLBACKS --- */
520 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
523 (void) navigation_action;
524 (void) policy_decision;
526 const gchar* uri = webkit_network_request_get_uri (request);
527 if (uzbl.state.verbose)
528 printf("New window requested -> %s \n", uri);
529 webkit_web_policy_decision_use(policy_decision);
534 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
539 /* If we can display it, let's display it... */
540 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
541 webkit_web_policy_decision_use (policy_decision);
545 /* ...everything we can't displayed is downloaded */
546 webkit_web_policy_decision_download (policy_decision);
551 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
555 if (uzbl.state.selected_url != NULL) {
556 if (uzbl.state.verbose)
557 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
558 new_window_load_uri(uzbl.state.selected_url);
560 if (uzbl.state.verbose)
561 printf("New web view -> %s\n","Nothing to open, exiting");
567 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
570 if (uzbl.behave.download_handler) {
571 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
572 if (uzbl.state.verbose)
573 printf("Download -> %s\n",uri);
574 /* if urls not escaped, we may have to escape and quote uri before this call */
575 run_handler(uzbl.behave.download_handler, uri);
580 /* scroll a bar in a given direction */
582 scroll (GtkAdjustment* bar, GArray *argv) {
586 gdouble page_size = gtk_adjustment_get_page_size(bar);
587 gdouble value = gtk_adjustment_get_value(bar);
588 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
591 value += page_size * amount * 0.01;
595 max_value = gtk_adjustment_get_upper(bar) - page_size;
597 if (value > max_value)
598 value = max_value; /* don't scroll past the end of the page */
600 gtk_adjustment_set_value (bar, value);
604 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
605 (void) page; (void) argv; (void) result;
606 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
610 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
611 (void) page; (void) argv; (void) result;
612 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
613 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
617 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
618 (void) page; (void) result;
619 scroll(uzbl.gui.bar_v, argv);
623 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
624 (void) page; (void) result;
625 scroll(uzbl.gui.bar_h, argv);
630 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
631 if(uzbl.state.verbose)
632 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
634 /* update geometry var with the actual geometry
635 this is necessary as some WMs don't seem to honour
636 the above setting and we don't want to end up with
637 wrong geometry information
644 if (!uzbl.behave.show_status) {
645 gtk_widget_hide(uzbl.gui.mainbar);
647 gtk_widget_show(uzbl.gui.mainbar);
653 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
658 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
662 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
667 if (uzbl.behave.show_status) {
668 gtk_widget_hide(uzbl.gui.mainbar);
670 gtk_widget_show(uzbl.gui.mainbar);
672 uzbl.behave.show_status = !uzbl.behave.show_status;
677 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
681 //Set selected_url state variable
682 g_free(uzbl.state.selected_url);
683 uzbl.state.selected_url = NULL;
685 uzbl.state.selected_url = g_strdup(link);
691 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
694 const gchar *title = webkit_web_view_get_title(web_view);
695 if (uzbl.gui.main_title)
696 g_free (uzbl.gui.main_title);
697 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
702 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
705 uzbl.gui.sbar.load_progress = progress;
707 g_free(uzbl.gui.sbar.progress_bar);
708 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
714 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
718 if (uzbl.behave.load_finish_handler)
719 run_handler(uzbl.behave.load_finish_handler, "");
722 void clear_keycmd() {
723 g_free(uzbl.state.keycmd);
724 uzbl.state.keycmd = g_strdup("");
728 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
732 uzbl.gui.sbar.load_progress = 0;
733 clear_keycmd(); // don't need old commands to remain on new page?
734 if (uzbl.behave.load_start_handler)
735 run_handler(uzbl.behave.load_start_handler, "");
739 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
742 g_free (uzbl.state.uri);
743 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
744 uzbl.state.uri = g_string_free (newuri, FALSE);
745 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
746 set_insert_mode(uzbl.behave.always_insert_mode);
749 if (uzbl.behave.load_commit_handler)
750 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
754 destroy_cb (GtkWidget* widget, gpointer data) {
762 if (uzbl.behave.history_handler) {
764 struct tm * timeinfo;
767 timeinfo = localtime ( &rawtime );
768 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
769 run_handler(uzbl.behave.history_handler, date);
774 /* VIEW funcs (little webkit wrappers) */
775 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
777 VIEWFUNC(reload_bypass_cache)
778 VIEWFUNC(stop_loading)
785 /* -- command to callback/function map for things we cannot attach to any signals */
786 struct {char *key; CommandInfo value;} cmdlist[] =
787 { /* key function no_split */
788 { "back", {view_go_back, 0} },
789 { "forward", {view_go_forward, 0} },
790 { "scroll_vert", {scroll_vert, 0} },
791 { "scroll_horz", {scroll_horz, 0} },
792 { "scroll_begin", {scroll_begin, 0} },
793 { "scroll_end", {scroll_end, 0} },
794 { "reload", {view_reload, 0}, },
795 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
796 { "stop", {view_stop_loading, 0}, },
797 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
798 { "zoom_out", {view_zoom_out, 0}, },
799 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
800 { "uri", {load_uri, TRUE} },
801 { "js", {run_js, TRUE} },
802 { "script", {run_external_js, 0} },
803 { "toggle_status", {toggle_status_cb, 0} },
804 { "spawn", {spawn, 0} },
805 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
806 { "sh", {spawn_sh, 0} },
807 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
808 { "exit", {close_uzbl, 0} },
809 { "search", {search_forward_text, TRUE} },
810 { "search_reverse", {search_reverse_text, TRUE} },
811 { "dehilight", {dehilight, 0} },
812 { "toggle_insert_mode", {toggle_insert_mode, 0} },
813 { "set", {set_var, TRUE} },
814 //{ "get", {get_var, TRUE} },
815 { "bind", {act_bind, TRUE} },
816 { "dump_config", {act_dump_config, 0} },
817 { "keycmd", {keycmd, TRUE} },
818 { "keycmd_nl", {keycmd_nl, TRUE} },
819 { "keycmd_bs", {keycmd_bs, 0} },
820 { "chain", {chain, 0} },
821 { "print", {print, TRUE} }
828 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
830 for (i = 0; i < LENGTH(cmdlist); i++)
831 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
834 /* -- CORE FUNCTIONS -- */
837 free_action(gpointer act) {
838 Action *action = (Action*)act;
839 g_free(action->name);
841 g_free(action->param);
846 new_action(const gchar *name, const gchar *param) {
847 Action *action = g_new(Action, 1);
849 action->name = g_strdup(name);
851 action->param = g_strdup(param);
853 action->param = NULL;
859 file_exists (const char * filename) {
860 return (access(filename, F_OK) == 0);
864 set_var(WebKitWebView *page, GArray *argv, GString *result) {
865 (void) page; (void) result;
866 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
867 if (split[0] != NULL) {
868 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
869 set_var_value(g_strstrip(split[0]), value);
876 print(WebKitWebView *page, GArray *argv, GString *result) {
877 (void) page; (void) result;
880 buf = expand(argv_idx(argv, 0), 0);
881 g_string_assign(result, buf);
886 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
887 (void) page; (void) result;
888 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
889 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
890 add_binding(g_strstrip(split[0]), value);
908 set_mode_indicator() {
909 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
910 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
915 set_mode_indicator();
920 set_insert_mode(gboolean mode) {
921 uzbl.behave.insert_mode = mode;
922 set_mode_indicator();
926 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
927 (void) page; (void) result;
929 if (argv_idx(argv, 0)) {
930 if (strcmp (argv_idx(argv, 0), "0") == 0) {
931 set_insert_mode(FALSE);
933 set_insert_mode(TRUE);
936 set_insert_mode( !uzbl.behave.insert_mode );
943 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
946 if (argv_idx(argv, 0)) {
947 GString* newuri = g_string_new (argv_idx(argv, 0));
948 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
949 run_js(web_view, argv, NULL);
952 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
953 g_string_prepend (newuri, "http://");
954 /* if we do handle cookies, ask our handler for them */
955 webkit_web_view_load_uri (web_view, newuri->str);
956 g_string_free (newuri, TRUE);
963 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
964 size_t argumentCount, const JSValueRef arguments[],
965 JSValueRef* exception) {
970 JSStringRef js_result_string;
971 GString *result = g_string_new("");
973 if (argumentCount >= 1) {
974 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
975 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
976 char ctl_line[arg_size];
977 JSStringGetUTF8CString(arg, ctl_line, arg_size);
979 parse_cmd_line(ctl_line, result);
981 JSStringRelease(arg);
983 js_result_string = JSStringCreateWithUTF8CString(result->str);
985 g_string_free(result, TRUE);
987 return JSValueMakeString(ctx, js_result_string);
990 JSStaticFunction js_static_functions[] = {
991 {"run", js_run_command, kJSPropertyAttributeNone},
996 /* This function creates the class and its definition, only once */
997 if (!uzbl.js.initialized) {
998 /* it would be pretty cool to make this dynamic */
999 uzbl.js.classdef = kJSClassDefinitionEmpty;
1000 uzbl.js.classdef.staticFunctions = js_static_functions;
1002 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1008 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1009 WebKitWebFrame *frame;
1010 JSGlobalContextRef context;
1011 JSObjectRef globalobject;
1012 JSStringRef var_name;
1014 JSStringRef js_script;
1015 JSValueRef js_result;
1016 JSStringRef js_result_string;
1017 size_t js_result_size;
1021 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1022 context = webkit_web_frame_get_global_context(frame);
1023 globalobject = JSContextGetGlobalObject(context);
1025 /* uzbl javascript namespace */
1026 var_name = JSStringCreateWithUTF8CString("Uzbl");
1027 JSObjectSetProperty(context, globalobject, var_name,
1028 JSObjectMake(context, uzbl.js.classref, NULL),
1029 kJSClassAttributeNone, NULL);
1031 /* evaluate the script and get return value*/
1032 js_script = JSStringCreateWithUTF8CString(script);
1033 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1034 if (js_result && !JSValueIsUndefined(context, js_result)) {
1035 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1036 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1038 if (js_result_size) {
1039 char js_result_utf8[js_result_size];
1040 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1041 g_string_assign(result, js_result_utf8);
1044 JSStringRelease(js_result_string);
1048 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1050 JSStringRelease(var_name);
1051 JSStringRelease(js_script);
1055 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1056 if (argv_idx(argv, 0))
1057 eval_js(web_view, argv_idx(argv, 0), result);
1061 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1063 if (argv_idx(argv, 0)) {
1064 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1069 while ((line = g_array_index(lines, gchar*, i))) {
1071 js = g_strdup (line);
1073 gchar* newjs = g_strconcat (js, line, NULL);
1080 if (uzbl.state.verbose)
1081 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1083 if (argv_idx (argv, 1)) {
1084 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1088 eval_js (web_view, js, result);
1090 g_array_free (lines, TRUE);
1095 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1096 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1097 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1098 webkit_web_view_unmark_text_matches (page);
1099 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1100 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1104 if (uzbl.state.searchtx) {
1105 if (uzbl.state.verbose)
1106 printf ("Searching: %s\n", uzbl.state.searchtx);
1107 webkit_web_view_set_highlight_text_matches (page, TRUE);
1108 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1113 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1115 search_text(page, argv, TRUE);
1119 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1121 search_text(page, argv, FALSE);
1125 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1126 (void) argv; (void) result;
1127 webkit_web_view_set_highlight_text_matches (page, FALSE);
1132 new_window_load_uri (const gchar * uri) {
1133 if (uzbl.behave.new_window) {
1134 GString *s = g_string_new ("");
1135 g_string_printf(s, "'%s'", uri);
1136 run_handler(uzbl.behave.new_window, s->str);
1139 GString* to_execute = g_string_new ("");
1140 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1142 for (i = 0; entries[i].long_name != NULL; i++) {
1143 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1144 gchar** str = (gchar**)entries[i].arg_data;
1146 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1150 if (uzbl.state.verbose)
1151 printf("\n%s\n", to_execute->str);
1152 g_spawn_command_line_async (to_execute->str, NULL);
1153 g_string_free (to_execute, TRUE);
1157 chain (WebKitWebView *page, GArray *argv, GString *result) {
1158 (void) page; (void) result;
1160 gchar **parts = NULL;
1162 while ((a = argv_idx(argv, i++))) {
1163 parts = g_strsplit (a, " ", 2);
1165 parse_command(parts[0], parts[1], result);
1171 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1175 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1181 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1185 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1191 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1196 int len = strlen(uzbl.state.keycmd);
1197 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1199 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1204 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1211 /* --Statusbar functions-- */
1213 build_progressbar_ascii(int percent) {
1214 int width=uzbl.gui.sbar.progress_w;
1217 GString *bar = g_string_new("");
1219 l = (double)percent*((double)width/100.);
1220 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1222 for(i=0; i<(int)l; i++)
1223 g_string_append(bar, uzbl.gui.sbar.progress_s);
1226 g_string_append(bar, uzbl.gui.sbar.progress_u);
1228 return g_string_free(bar, FALSE);
1230 /* --End Statusbar functions-- */
1233 sharg_append(GArray *a, const gchar *str) {
1234 const gchar *s = (str ? str : "");
1235 g_array_append_val(a, s);
1238 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1240 run_command (const gchar *command, const guint npre, const gchar **args,
1241 const gboolean sync, char **output_stdout) {
1242 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1245 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1246 gchar *pid = itos(getpid());
1247 gchar *xwin = itos(uzbl.xwin);
1249 sharg_append(a, command);
1250 for (i = 0; i < npre; i++) /* add n args before the default vars */
1251 sharg_append(a, args[i]);
1252 sharg_append(a, uzbl.state.config_file);
1253 sharg_append(a, pid);
1254 sharg_append(a, xwin);
1255 sharg_append(a, uzbl.comm.fifo_path);
1256 sharg_append(a, uzbl.comm.socket_path);
1257 sharg_append(a, uzbl.state.uri);
1258 sharg_append(a, uzbl.gui.main_title);
1260 for (i = npre; i < g_strv_length((gchar**)args); i++)
1261 sharg_append(a, args[i]);
1265 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1267 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1268 NULL, NULL, output_stdout, NULL, NULL, &err);
1269 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1270 NULL, NULL, NULL, &err);
1272 if (uzbl.state.verbose) {
1273 GString *s = g_string_new("spawned:");
1274 for (i = 0; i < (a->len); i++) {
1275 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1276 g_string_append_printf(s, " %s", qarg);
1279 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1280 printf("%s\n", s->str);
1281 g_string_free(s, TRUE);
1283 printf("Stdout: %s\n", *output_stdout);
1287 g_printerr("error on run_command: %s\n", err->message);
1292 g_array_free (a, TRUE);
1297 split_quoted(const gchar* src, const gboolean unquote) {
1298 /* split on unquoted space, return array of strings;
1299 remove a layer of quotes and backslashes if unquote */
1300 if (!src) return NULL;
1302 gboolean dq = FALSE;
1303 gboolean sq = FALSE;
1304 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1305 GString *s = g_string_new ("");
1309 for (p = src; *p != '\0'; p++) {
1310 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1311 else if (*p == '\\') { g_string_append_c(s, *p++);
1312 g_string_append_c(s, *p); }
1313 else if ((*p == '"') && unquote && !sq) dq = !dq;
1314 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1316 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1317 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1319 else if ((*p == ' ') && !dq && !sq) {
1320 dup = g_strdup(s->str);
1321 g_array_append_val(a, dup);
1322 g_string_truncate(s, 0);
1323 } else g_string_append_c(s, *p);
1325 dup = g_strdup(s->str);
1326 g_array_append_val(a, dup);
1327 ret = (gchar**)a->data;
1328 g_array_free (a, FALSE);
1329 g_string_free (s, TRUE);
1334 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1335 (void)web_view; (void)result;
1336 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1337 if (argv_idx(argv, 0))
1338 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1342 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1343 (void)web_view; (void)result;
1345 if (argv_idx(argv, 0))
1346 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1347 TRUE, &uzbl.comm.sync_stdout);
1351 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1352 (void)web_view; (void)result;
1353 if (!uzbl.behave.shell_cmd) {
1354 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1359 gchar *spacer = g_strdup("");
1360 g_array_insert_val(argv, 1, spacer);
1361 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1363 for (i = 1; i < g_strv_length(cmd); i++)
1364 g_array_prepend_val(argv, cmd[i]);
1366 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1372 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1373 (void)web_view; (void)result;
1374 if (!uzbl.behave.shell_cmd) {
1375 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1380 gchar *spacer = g_strdup("");
1381 g_array_insert_val(argv, 1, spacer);
1382 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1384 for (i = 1; i < g_strv_length(cmd); i++)
1385 g_array_prepend_val(argv, cmd[i]);
1387 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1388 TRUE, &uzbl.comm.sync_stdout);
1394 parse_command(const char *cmd, const char *param, GString *result) {
1397 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1399 gchar **par = split_quoted(param, TRUE);
1400 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1402 if (c->no_split) { /* don't split */
1403 sharg_append(a, param);
1405 for (i = 0; i < g_strv_length(par); i++)
1406 sharg_append(a, par[i]);
1409 if (result == NULL) {
1410 GString *result_print = g_string_new("");
1412 c->function(uzbl.gui.web_view, a, result_print);
1413 if (result_print->len)
1414 printf("%*s\n", result_print->len, result_print->str);
1416 g_string_free(result_print, TRUE);
1418 c->function(uzbl.gui.web_view, a, result);
1421 g_array_free (a, TRUE);
1424 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1431 if(*uzbl.net.proxy_url == ' '
1432 || uzbl.net.proxy_url == NULL) {
1433 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1434 (GType) SOUP_SESSION_PROXY_URI);
1437 suri = soup_uri_new(uzbl.net.proxy_url);
1438 g_object_set(G_OBJECT(uzbl.net.soup_session),
1439 SOUP_SESSION_PROXY_URI,
1441 soup_uri_free(suri);
1448 if(file_exists(uzbl.gui.icon)) {
1449 if (uzbl.gui.main_window)
1450 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1452 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1458 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1459 g_array_append_val (a, uzbl.state.uri);
1460 load_uri(uzbl.gui.web_view, a, NULL);
1461 g_array_free (a, TRUE);
1465 cmd_always_insert_mode() {
1466 set_insert_mode(uzbl.behave.always_insert_mode);
1472 g_object_set(G_OBJECT(uzbl.net.soup_session),
1473 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1477 cmd_max_conns_host() {
1478 g_object_set(G_OBJECT(uzbl.net.soup_session),
1479 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1484 soup_session_remove_feature
1485 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1486 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1487 /*g_free(uzbl.net.soup_logger);*/
1489 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1490 soup_session_add_feature(uzbl.net.soup_session,
1491 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1496 return webkit_web_view_get_settings(uzbl.gui.web_view);
1501 WebKitWebSettings *ws = view_settings();
1502 if (uzbl.behave.font_size > 0) {
1503 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1506 if (uzbl.behave.monospace_size > 0) {
1507 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1508 uzbl.behave.monospace_size, NULL);
1510 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1511 uzbl.behave.font_size, NULL);
1517 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1521 cmd_disable_plugins() {
1522 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1523 !uzbl.behave.disable_plugins, NULL);
1527 cmd_disable_scripts() {
1528 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1529 !uzbl.behave.disable_scripts, NULL);
1533 cmd_minimum_font_size() {
1534 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1535 uzbl.behave.minimum_font_size, NULL);
1538 cmd_autoload_img() {
1539 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1540 uzbl.behave.autoload_img, NULL);
1545 cmd_autoshrink_img() {
1546 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1547 uzbl.behave.autoshrink_img, NULL);
1552 cmd_enable_spellcheck() {
1553 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1554 uzbl.behave.enable_spellcheck, NULL);
1558 cmd_enable_private() {
1559 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1560 uzbl.behave.enable_private, NULL);
1565 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1566 uzbl.behave.print_bg, NULL);
1571 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1572 uzbl.behave.style_uri, NULL);
1576 cmd_resizable_txt() {
1577 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1578 uzbl.behave.resizable_txt, NULL);
1582 cmd_default_encoding() {
1583 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1584 uzbl.behave.default_encoding, NULL);
1588 cmd_enforce_96dpi() {
1589 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1590 uzbl.behave.enforce_96dpi, NULL);
1594 cmd_caret_browsing() {
1595 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1596 uzbl.behave.caret_browsing, NULL);
1600 cmd_cookie_handler() {
1601 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1602 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1603 if ((g_strcmp0(split[0], "sh") == 0) ||
1604 (g_strcmp0(split[0], "spawn") == 0)) {
1605 g_free (uzbl.behave.cookie_handler);
1606 uzbl.behave.cookie_handler =
1607 g_strdup_printf("sync_%s %s", split[0], split[1]);
1614 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1615 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1616 if ((g_strcmp0(split[0], "sh") == 0) ||
1617 (g_strcmp0(split[0], "spawn") == 0)) {
1618 g_free (uzbl.behave.new_window);
1619 uzbl.behave.new_window =
1620 g_strdup_printf("%s %s", split[0], split[1]);
1627 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1632 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1637 if(uzbl.behave.inject_html) {
1638 webkit_web_view_load_html_string (uzbl.gui.web_view,
1639 uzbl.behave.inject_html, NULL);
1648 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1649 uzbl.behave.modmask = 0;
1651 if(uzbl.behave.modkey)
1652 g_free(uzbl.behave.modkey);
1653 uzbl.behave.modkey = buf;
1655 for (i = 0; modkeys[i].key != NULL; i++) {
1656 if (g_strrstr(buf, modkeys[i].key))
1657 uzbl.behave.modmask |= modkeys[i].mask;
1663 if (*uzbl.net.useragent == ' ') {
1664 g_free (uzbl.net.useragent);
1665 uzbl.net.useragent = NULL;
1667 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1668 uzbl.net.useragent, NULL);
1674 gtk_widget_ref(uzbl.gui.scrolled_win);
1675 gtk_widget_ref(uzbl.gui.mainbar);
1676 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1677 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1679 if(uzbl.behave.status_top) {
1680 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1681 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1684 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1685 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1687 gtk_widget_unref(uzbl.gui.scrolled_win);
1688 gtk_widget_unref(uzbl.gui.mainbar);
1689 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1694 set_var_value(gchar *name, gchar *val) {
1695 uzbl_cmdprop *c = NULL;
1699 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1700 if(!c->writeable) return FALSE;
1702 /* check for the variable type */
1703 if (c->type == TYPE_STR) {
1704 buf = expand(val, 0);
1707 } else if(c->type == TYPE_INT) {
1708 int *ip = (int *)c->ptr;
1709 buf = expand(val, 0);
1710 *ip = (int)strtoul(buf, &endp, 10);
1712 } else if (c->type == TYPE_FLOAT) {
1713 float *fp = (float *)c->ptr;
1714 buf = expand(val, 0);
1715 *fp = strtod(buf, &endp);
1719 /* invoke a command specific function */
1720 if(c->func) c->func();
1727 Behaviour *b = &uzbl.behave;
1729 if(b->html_buffer->str) {
1730 webkit_web_view_load_html_string (uzbl.gui.web_view,
1731 b->html_buffer->str, b->base_url);
1732 g_string_free(b->html_buffer, TRUE);
1733 b->html_buffer = g_string_new("");
1737 enum {M_CMD, M_HTML};
1739 parse_cmd_line(const char *ctl_line, GString *result) {
1740 Behaviour *b = &uzbl.behave;
1743 if(b->mode == M_HTML) {
1744 len = strlen(b->html_endmarker);
1745 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1746 if(len == strlen(ctl_line)-1 &&
1747 !strncmp(b->html_endmarker, ctl_line, len)) {
1749 set_var_value("mode", "0");
1754 set_timeout(b->html_timeout);
1755 g_string_append(b->html_buffer, ctl_line);
1758 else if((ctl_line[0] == '#') /* Comments */
1759 || (ctl_line[0] == ' ')
1760 || (ctl_line[0] == '\n'))
1761 ; /* ignore these lines */
1762 else { /* parse a command */
1764 gchar **tokens = NULL;
1765 len = strlen(ctl_line);
1767 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1768 ctlstrip = g_strndup(ctl_line, len - 1);
1769 else ctlstrip = g_strdup(ctl_line);
1771 tokens = g_strsplit(ctlstrip, " ", 2);
1772 parse_command(tokens[0], tokens[1], result);
1779 build_stream_name(int type, const gchar* dir) {
1780 State *s = &uzbl.state;
1784 str = g_strdup_printf
1785 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1786 } else if (type == SOCKET) {
1787 str = g_strdup_printf
1788 ("%s/uzbl_socket_%s", dir, s->instance_name);
1794 control_fifo(GIOChannel *gio, GIOCondition condition) {
1795 if (uzbl.state.verbose)
1796 printf("triggered\n");
1801 if (condition & G_IO_HUP)
1802 g_error ("Fifo: Read end of pipe died!\n");
1805 g_error ("Fifo: GIOChannel broke\n");
1807 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1808 if (ret == G_IO_STATUS_ERROR) {
1809 g_error ("Fifo: Error reading: %s\n", err->message);
1813 parse_cmd_line(ctl_line, NULL);
1820 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1821 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1822 if (unlink(uzbl.comm.fifo_path) == -1)
1823 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1824 g_free(uzbl.comm.fifo_path);
1825 uzbl.comm.fifo_path = NULL;
1828 GIOChannel *chan = NULL;
1829 GError *error = NULL;
1830 gchar *path = build_stream_name(FIFO, dir);
1832 if (!file_exists(path)) {
1833 if (mkfifo (path, 0666) == 0) {
1834 // 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.
1835 chan = g_io_channel_new_file(path, "r+", &error);
1837 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1838 if (uzbl.state.verbose)
1839 printf ("init_fifo: created successfully as %s\n", path);
1840 uzbl.comm.fifo_path = path;
1842 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1843 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1844 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1845 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1847 /* if we got this far, there was an error; cleanup */
1848 if (error) g_error_free (error);
1855 control_stdin(GIOChannel *gio, GIOCondition condition) {
1857 gchar *ctl_line = NULL;
1860 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1861 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1864 parse_cmd_line(ctl_line, NULL);
1872 GIOChannel *chan = NULL;
1873 GError *error = NULL;
1875 chan = g_io_channel_unix_new(fileno(stdin));
1877 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1878 g_error ("Stdin: could not add watch\n");
1880 if (uzbl.state.verbose)
1881 printf ("Stdin: watch added successfully\n");
1884 g_error ("Stdin: Error while opening: %s\n", error->message);
1886 if (error) g_error_free (error);
1890 control_socket(GIOChannel *chan) {
1891 struct sockaddr_un remote;
1892 unsigned int t = sizeof(remote);
1894 GIOChannel *clientchan;
1896 clientsock = accept (g_io_channel_unix_get_fd(chan),
1897 (struct sockaddr *) &remote, &t);
1899 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1900 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1901 (GIOFunc) control_client_socket, clientchan);
1908 control_client_socket(GIOChannel *clientchan) {
1910 GString *result = g_string_new("");
1911 GError *error = NULL;
1915 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1916 if (ret == G_IO_STATUS_ERROR) {
1917 g_warning ("Error reading: %s\n", error->message);
1918 g_io_channel_shutdown(clientchan, TRUE, &error);
1920 } else if (ret == G_IO_STATUS_EOF) {
1921 /* shutdown and remove channel watch from main loop */
1922 g_io_channel_shutdown(clientchan, TRUE, &error);
1927 parse_cmd_line (ctl_line, result);
1928 g_string_append_c(result, '\n');
1929 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1931 if (ret == G_IO_STATUS_ERROR) {
1932 g_warning ("Error writing: %s", error->message);
1934 g_io_channel_flush(clientchan, &error);
1937 if (error) g_error_free (error);
1938 g_string_free(result, TRUE);
1944 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1945 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1946 if (unlink(uzbl.comm.socket_path) == -1)
1947 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1948 g_free(uzbl.comm.socket_path);
1949 uzbl.comm.socket_path = NULL;
1957 GIOChannel *chan = NULL;
1959 struct sockaddr_un local;
1960 gchar *path = build_stream_name(SOCKET, dir);
1962 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1964 local.sun_family = AF_UNIX;
1965 strcpy (local.sun_path, path);
1966 unlink (local.sun_path);
1968 len = strlen (local.sun_path) + sizeof (local.sun_family);
1969 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1970 if (uzbl.state.verbose)
1971 printf ("init_socket: opened in %s\n", path);
1974 if( (chan = g_io_channel_unix_new(sock)) ) {
1975 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1976 uzbl.comm.socket_path = path;
1979 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1981 /* if we got this far, there was an error; cleanup */
1988 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1989 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1991 // this function may be called very early when the templates are not set (yet), hence the checks
1993 update_title (void) {
1994 Behaviour *b = &uzbl.behave;
1997 if (b->show_status) {
1998 if (b->title_format_short) {
1999 parsed = expand(b->title_format_short, 0);
2000 if (uzbl.gui.main_window)
2001 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2004 if (b->status_format) {
2005 parsed = expand(b->status_format, 0);
2006 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2009 if (b->status_background) {
2011 gdk_color_parse (b->status_background, &color);
2012 //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)
2013 if (uzbl.gui.main_window)
2014 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2015 else if (uzbl.gui.plug)
2016 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2019 if (b->title_format_long) {
2020 parsed = expand(b->title_format_long, 0);
2021 if (uzbl.gui.main_window)
2022 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2029 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2033 retreive_geometry();
2038 key_press_cb (GtkWidget* window, GdkEventKey* event)
2040 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2044 if (event->type != GDK_KEY_PRESS ||
2045 event->keyval == GDK_Page_Up ||
2046 event->keyval == GDK_Page_Down ||
2047 event->keyval == GDK_Up ||
2048 event->keyval == GDK_Down ||
2049 event->keyval == GDK_Left ||
2050 event->keyval == GDK_Right ||
2051 event->keyval == GDK_Shift_L ||
2052 event->keyval == GDK_Shift_R)
2055 /* turn off insert mode (if always_insert_mode is not used) */
2056 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2057 set_insert_mode(uzbl.behave.always_insert_mode);
2062 if (uzbl.behave.insert_mode &&
2063 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2064 (!uzbl.behave.modmask)
2069 if (event->keyval == GDK_Escape) {
2072 dehilight(uzbl.gui.web_view, NULL, NULL);
2076 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2077 if (event->keyval == GDK_Insert) {
2079 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2080 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2082 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2085 GString* keycmd = g_string_new(uzbl.state.keycmd);
2086 g_string_append (keycmd, str);
2087 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2094 if (event->keyval == GDK_BackSpace)
2095 keycmd_bs(NULL, NULL, NULL);
2097 gboolean key_ret = FALSE;
2098 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2101 GString* keycmd = g_string_new(uzbl.state.keycmd);
2102 g_string_append(keycmd, event->string);
2103 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2106 run_keycmd(key_ret);
2108 if (key_ret) return (!uzbl.behave.insert_mode);
2113 run_keycmd(const gboolean key_ret) {
2114 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2116 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2118 parse_command(act->name, act->param, NULL);
2122 /* try if it's an incremental keycmd or one that takes args, and run it */
2123 GString* short_keys = g_string_new ("");
2124 GString* short_keys_inc = g_string_new ("");
2126 guint len = strlen(uzbl.state.keycmd);
2127 for (i=0; i<len; i++) {
2128 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2129 g_string_assign(short_keys_inc, short_keys->str);
2130 g_string_append_c(short_keys, '_');
2131 g_string_append_c(short_keys_inc, '*');
2133 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2134 /* run normal cmds only if return was pressed */
2135 exec_paramcmd(act, i);
2138 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2139 if (key_ret) /* just quit the incremental command on return */
2141 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2145 g_string_truncate(short_keys, short_keys->len - 1);
2147 g_string_free (short_keys, TRUE);
2148 g_string_free (short_keys_inc, TRUE);
2152 exec_paramcmd(const Action *act, const guint i) {
2153 GString *parampart = g_string_new (uzbl.state.keycmd);
2154 GString *actionname = g_string_new ("");
2155 GString *actionparam = g_string_new ("");
2156 g_string_erase (parampart, 0, i+1);
2158 g_string_printf (actionname, act->name, parampart->str);
2160 g_string_printf (actionparam, act->param, parampart->str);
2161 parse_command(actionname->str, actionparam->str, NULL);
2162 g_string_free(actionname, TRUE);
2163 g_string_free(actionparam, TRUE);
2164 g_string_free(parampart, TRUE);
2172 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2173 //main_window_ref = g_object_ref(scrolled_window);
2174 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2176 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2177 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2179 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2180 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2181 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2182 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2183 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2184 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2189 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2191 return scrolled_window;
2198 g->mainbar = gtk_hbox_new (FALSE, 0);
2200 /* keep a reference to the bar so we can re-pack it at runtime*/
2201 //sbar_ref = g_object_ref(g->mainbar);
2203 g->mainbar_label = gtk_label_new ("");
2204 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2205 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2206 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2207 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2208 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2209 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2215 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2216 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2217 gtk_widget_set_name (window, "Uzbl browser");
2218 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2219 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2220 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2227 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2228 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2229 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2236 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2238 If actname is one that calls an external command, this function will inject
2239 newargs in front of the user-provided args in that command line. They will
2240 come become after the body of the script (in sh) or after the name of
2241 the command to execute (in spawn).
2242 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2243 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2245 The return value consist of two strings: the action (sh, ...) and its args.
2247 If act is not one that calls an external command, then the given action merely
2250 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2251 /* Arrr! Here be memory leaks */
2252 gchar *actdup = g_strdup(actname);
2253 g_array_append_val(rets, actdup);
2255 if ((g_strcmp0(actname, "spawn") == 0) ||
2256 (g_strcmp0(actname, "sh") == 0) ||
2257 (g_strcmp0(actname, "sync_spawn") == 0) ||
2258 (g_strcmp0(actname, "sync_sh") == 0)) {
2260 GString *a = g_string_new("");
2261 gchar **spawnparts = split_quoted(origargs, FALSE);
2262 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2263 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2265 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2266 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2268 g_array_append_val(rets, a->str);
2269 g_string_free(a, FALSE);
2270 g_strfreev(spawnparts);
2272 gchar *origdup = g_strdup(origargs);
2273 g_array_append_val(rets, origdup);
2275 return (gchar**)g_array_free(rets, FALSE);
2279 run_handler (const gchar *act, const gchar *args) {
2280 /* Consider this code a temporary hack to make the handlers usable.
2281 In practice, all this splicing, injection, and reconstruction is
2282 inefficient, annoying and hard to manage. Potential pitfalls arise
2283 when the handler specific args 1) are not quoted (the handler
2284 callbacks should take care of this) 2) are quoted but interfere
2285 with the users' own quotation. A more ideal solution is
2286 to refactor parse_command so that it doesn't just take a string
2287 and execute it; rather than that, we should have a function which
2288 returns the argument vector parsed from the string. This vector
2289 could be modified (e.g. insert additional args into it) before
2290 passing it to the next function that actually executes it. Though
2291 it still isn't perfect for chain actions.. will reconsider & re-
2292 factor when I have the time. -duc */
2294 char **parts = g_strsplit(act, " ", 2);
2296 if (g_strcmp0(parts[0], "chain") == 0) {
2297 GString *newargs = g_string_new("");
2298 gchar **chainparts = split_quoted(parts[1], FALSE);
2300 /* for every argument in the chain, inject the handler args
2301 and make sure the new parts are wrapped in quotes */
2302 gchar **cp = chainparts;
2304 gchar *quotless = NULL;
2305 gchar **spliced_quotless = NULL; // sigh -_-;
2306 gchar **inpart = NULL;
2309 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2311 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2312 } else quotless = g_strdup(*cp);
2314 spliced_quotless = g_strsplit(quotless, " ", 2);
2315 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2316 g_strfreev(spliced_quotless);
2318 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2324 parse_command(parts[0], &(newargs->str[1]), NULL);
2325 g_string_free(newargs, TRUE);
2326 g_strfreev(chainparts);
2329 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2330 parse_command(inparts[0], inparts[1], NULL);
2338 add_binding (const gchar *key, const gchar *act) {
2339 char **parts = g_strsplit(act, " ", 2);
2346 if (uzbl.state.verbose)
2347 printf ("Binding %-10s : %s\n", key, act);
2348 action = new_action(parts[0], parts[1]);
2350 if (g_hash_table_remove (uzbl.bindings, key))
2351 g_warning ("Overwriting existing binding for \"%s\"", key);
2352 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2357 get_xdg_var (XDG_Var xdg) {
2358 const gchar* actual_value = getenv (xdg.environmental);
2359 const gchar* home = getenv ("HOME");
2360 gchar* return_value;
2362 if (! actual_value || strcmp (actual_value, "") == 0) {
2363 if (xdg.default_value) {
2364 return_value = str_replace ("~", home, xdg.default_value);
2366 return_value = NULL;
2369 return_value = str_replace("~", home, actual_value);
2372 return return_value;
2376 find_xdg_file (int xdg_type, char* filename) {
2377 /* xdg_type = 0 => config
2378 xdg_type = 1 => data
2379 xdg_type = 2 => cache*/
2381 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2382 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2385 gchar* temporary_string;
2389 if (! file_exists (temporary_file) && xdg_type != 2) {
2390 buf = get_xdg_var (XDG[3 + xdg_type]);
2391 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2394 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2395 g_free (temporary_file);
2396 temporary_file = g_strconcat (temporary_string, filename, NULL);
2400 //g_free (temporary_string); - segfaults.
2402 if (file_exists (temporary_file)) {
2403 return temporary_file;
2410 State *s = &uzbl.state;
2411 Network *n = &uzbl.net;
2413 for (i = 0; default_config[i].command != NULL; i++) {
2414 parse_cmd_line(default_config[i].command, NULL);
2417 if (g_strcmp0(s->config_file, "-") == 0) {
2418 s->config_file = NULL;
2422 else if (!s->config_file) {
2423 s->config_file = find_xdg_file (0, "/uzbl/config");
2426 if (s->config_file) {
2427 GArray* lines = read_file_by_line (s->config_file);
2431 while ((line = g_array_index(lines, gchar*, i))) {
2432 parse_cmd_line (line, NULL);
2436 g_array_free (lines, TRUE);
2438 if (uzbl.state.verbose)
2439 printf ("No configuration file loaded.\n");
2442 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2445 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2448 if (!uzbl.behave.cookie_handler)
2451 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2452 GString *s = g_string_new ("");
2453 SoupURI * soup_uri = soup_message_get_uri(msg);
2454 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2455 run_handler(uzbl.behave.cookie_handler, s->str);
2457 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2458 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2459 if ( p != NULL ) *p = '\0';
2460 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2462 if (uzbl.comm.sync_stdout)
2463 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2465 g_string_free(s, TRUE);
2469 save_cookies (SoupMessage *msg, gpointer user_data){
2473 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2474 cookie = soup_cookie_to_set_cookie_header(ck->data);
2475 SoupURI * soup_uri = soup_message_get_uri(msg);
2476 GString *s = g_string_new ("");
2477 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2478 run_handler(uzbl.behave.cookie_handler, s->str);
2480 g_string_free(s, TRUE);
2485 /* --- WEBINSPECTOR --- */
2487 hide_window_cb(GtkWidget *widget, gpointer data) {
2490 gtk_widget_hide(widget);
2494 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2497 (void) web_inspector;
2498 GtkWidget* scrolled_window;
2499 GtkWidget* new_web_view;
2502 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2503 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2504 G_CALLBACK(hide_window_cb), NULL);
2506 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2507 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2508 gtk_widget_show(g->inspector_window);
2510 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2511 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2512 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2513 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2514 gtk_widget_show(scrolled_window);
2516 new_web_view = webkit_web_view_new();
2517 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2519 return WEBKIT_WEB_VIEW(new_web_view);
2523 inspector_show_window_cb (WebKitWebInspector* inspector){
2525 gtk_widget_show(uzbl.gui.inspector_window);
2529 /* TODO: Add variables and code to make use of these functions */
2531 inspector_close_window_cb (WebKitWebInspector* inspector){
2537 inspector_attach_window_cb (WebKitWebInspector* inspector){
2543 inspector_detach_window_cb (WebKitWebInspector* inspector){
2549 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2555 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2561 set_up_inspector() {
2563 WebKitWebSettings *settings = view_settings();
2564 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2566 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2567 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2568 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2569 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2570 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2571 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2574 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2578 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2580 uzbl_cmdprop *c = v;
2585 if(c->type == TYPE_STR)
2586 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2587 else if(c->type == TYPE_INT)
2588 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2589 else if(c->type == TYPE_FLOAT)
2590 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2594 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2598 printf("bind %s = %s %s\n", (char *)k ,
2599 (char *)a->name, a->param?(char *)a->param:"");
2604 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2605 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2609 retreive_geometry() {
2611 GString *buf = g_string_new("");
2613 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2614 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2616 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2618 if(uzbl.gui.geometry)
2619 g_free(uzbl.gui.geometry);
2620 uzbl.gui.geometry = g_string_free(buf, FALSE);
2623 /* set up gtk, gobject, variable defaults and other things that tests and other
2624 * external applications need to do anyhow */
2626 initialize(int argc, char *argv[]) {
2627 gtk_init (&argc, &argv);
2628 if (!g_thread_supported ())
2629 g_thread_init (NULL);
2630 uzbl.state.executable_path = g_strdup(argv[0]);
2631 uzbl.state.selected_url = NULL;
2632 uzbl.state.searchtx = NULL;
2634 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2635 g_option_context_add_main_entries (context, entries, NULL);
2636 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2637 g_option_context_parse (context, &argc, &argv, NULL);
2638 g_option_context_free(context);
2640 if (uzbl.behave.print_version) {
2641 printf("Commit: %s\n", COMMIT);
2645 /* initialize hash table */
2646 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2648 uzbl.net.soup_session = webkit_get_default_session();
2649 uzbl.state.keycmd = g_strdup("");
2651 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2652 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2653 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2654 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2655 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2656 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2658 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2659 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2660 uzbl.gui.sbar.progress_w = 10;
2662 /* HTML mode defaults*/
2663 uzbl.behave.html_buffer = g_string_new("");
2664 uzbl.behave.html_endmarker = g_strdup(".");
2665 uzbl.behave.html_timeout = 60;
2666 uzbl.behave.base_url = g_strdup("http://invalid");
2668 /* default mode indicators */
2669 uzbl.behave.insert_indicator = g_strdup("I");
2670 uzbl.behave.cmd_indicator = g_strdup("C");
2672 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2673 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2674 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2675 uzbl.info.arch = ARCH;
2676 uzbl.info.commit = COMMIT;
2679 make_var_to_name_hash();
2681 uzbl.gui.scrolled_win = create_browser();
2684 #ifndef UZBL_LIBRARY
2687 main (int argc, char* argv[]) {
2688 initialize(argc, argv);
2690 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2694 /* initial packing */
2695 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2696 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2698 if (uzbl.state.socket_id) {
2699 uzbl.gui.plug = create_plug ();
2700 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2701 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2703 uzbl.gui.main_window = create_window ();
2704 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2705 gtk_widget_show_all (uzbl.gui.main_window);
2706 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2709 if(!uzbl.state.instance_name)
2710 uzbl.state.instance_name = itos((int)uzbl.xwin);
2712 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2714 if (uzbl.state.verbose) {
2715 printf("Uzbl start location: %s\n", argv[0]);
2716 if (uzbl.state.socket_id)
2717 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2719 printf("window_id %i\n",(int) uzbl.xwin);
2720 printf("pid %i\n", getpid ());
2721 printf("name: %s\n", uzbl.state.instance_name);
2724 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2725 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2726 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2727 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2728 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2730 if(uzbl.gui.geometry)
2733 retreive_geometry();
2735 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2736 if (argc > 1 && !uzbl.state.uri)
2737 uri_override = g_strdup(argv[1]);
2738 gboolean verbose_override = uzbl.state.verbose;
2741 set_insert_mode(FALSE);
2743 if (!uzbl.behave.show_status)
2744 gtk_widget_hide(uzbl.gui.mainbar);
2751 if (verbose_override > uzbl.state.verbose)
2752 uzbl.state.verbose = verbose_override;
2755 set_var_value("uri", uri_override);
2756 g_free(uri_override);
2757 } else if (uzbl.state.uri)
2763 return EXIT_SUCCESS;
2767 /* vi: set et ts=4: */