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>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
77 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
78 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
79 "Print the version and exit", NULL },
80 { NULL, 0, 0, 0, NULL, NULL, NULL }
83 /* associate command names to their properties */
84 typedef const struct {
85 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
86 the PTR() macro is kind of preventing this change at the moment. */
94 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
96 /* abbreviations to help keep the table's width humane */
97 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
98 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
103 } var_name_to_ptr[] = {
104 /* variable name pointer to variable in code type dump callback function */
105 /* ---------------------------------------------------------------------------------------------- */
106 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
107 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
108 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
109 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
110 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
111 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
112 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
113 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
114 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
115 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
116 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
117 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
118 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
119 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
120 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
121 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
122 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
124 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
125 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
126 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
127 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
128 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
129 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
130 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
131 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
132 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
133 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
134 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
135 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
136 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
137 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
138 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
139 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
140 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
141 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
142 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
143 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
144 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
145 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
147 /* exported WebKitWebSettings properties */
148 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
149 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
150 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
151 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
152 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
153 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
154 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
155 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
156 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
157 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
158 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
159 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
160 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
161 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
162 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
163 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
165 /* constants (not dumpable or writeable) */
166 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
167 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
168 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
169 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
170 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
171 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
172 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
173 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
174 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
175 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
176 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
178 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
179 }, *n2v_p = var_name_to_ptr;
186 { "SHIFT", GDK_SHIFT_MASK }, // shift
187 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
188 { "CONTROL", GDK_CONTROL_MASK }, // control
189 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
190 { "MOD2", GDK_MOD2_MASK }, // 5th mod
191 { "MOD3", GDK_MOD3_MASK }, // 6th mod
192 { "MOD4", GDK_MOD4_MASK }, // 7th mod
193 { "MOD5", GDK_MOD5_MASK }, // 8th mod
194 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
195 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
196 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
197 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
198 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
199 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
200 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
201 { "META", GDK_META_MASK }, // meta (since 2.10)
206 /* construct a hash from the var_name_to_ptr array for quick access */
208 make_var_to_name_hash() {
209 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
211 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
216 /* --- UTILITY FUNCTIONS --- */
217 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
219 get_exp_type(gchar *s) {
223 else if(*(s+1) == '{')
224 return EXP_BRACED_VAR;
225 else if(*(s+1) == '<')
227 else if(*(s+1) == '[')
230 return EXP_SIMPLE_VAR;
236 * recurse == 1: don't expand '@(command)@'
237 * recurse == 2: don't expand '@<java script>@'
240 expand(char *s, guint recurse) {
244 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, upto);
273 if(!vend) vend = strchr(s, '\0');
277 strcpy(str_end, ")@");
279 vend = strstr(s, str_end);
280 if(!vend) vend = strchr(s, '\0');
284 strcpy(str_end, ">@");
286 vend = strstr(s, str_end);
287 if(!vend) vend = strchr(s, '\0');
291 strcpy(str_end, "]@");
293 vend = strstr(s, str_end);
294 if(!vend) vend = strchr(s, '\0');
299 strncpy(ret, s, vend-s);
303 if(etype == EXP_SIMPLE_VAR ||
304 etype == EXP_BRACED_VAR) {
305 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
306 if(c->type == TYPE_STR && *c->ptr != NULL) {
307 g_string_append(buf, (gchar *)*c->ptr);
308 } else if(c->type == TYPE_INT) {
309 g_string_append_printf(buf, "%d", (int)*c->ptr);
311 else if(c->type == TYPE_FLOAT) {
312 g_string_append_printf(buf, "%f", *(float *)c->ptr);
316 if(etype == EXP_SIMPLE_VAR)
321 else if(recurse != 1 &&
323 mycmd = expand(ret, 1);
324 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
328 g_printerr("error on running command: %s\n", err->message);
331 else if (*cmd_stdout) {
332 int len = strlen(cmd_stdout);
334 if(cmd_stdout[len-1] == '\n')
335 cmd_stdout[--len] = 0; /* strip trailing newline */
337 g_string_append(buf, cmd_stdout);
342 else if(recurse != 2 &&
344 mycmd = expand(ret, 2);
345 eval_js(uzbl.gui.web_view, mycmd, js_ret);
349 g_string_append(buf, js_ret->str);
350 g_string_free(js_ret, TRUE);
351 js_ret = g_string_new("");
355 else if(etype == EXP_ESCAPE) {
356 mycmd = expand(ret, 0);
357 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
359 g_string_append(buf, escaped);
368 g_string_append_c(buf, *s);
373 g_string_free(js_ret, TRUE);
374 return g_string_free(buf, FALSE);
381 snprintf(tmp, sizeof(tmp), "%i", val);
382 return g_strdup(tmp);
386 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
389 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
392 str_replace (const char* search, const char* replace, const char* string) {
396 buf = g_strsplit (string, search, -1);
397 ret = g_strjoinv (replace, buf);
398 g_strfreev(buf); // somebody said this segfaults
404 read_file_by_line (gchar *path) {
405 GIOChannel *chan = NULL;
406 gchar *readbuf = NULL;
408 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
411 chan = g_io_channel_new_file(path, "r", NULL);
414 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
415 const gchar* val = g_strdup (readbuf);
416 g_array_append_val (lines, val);
421 g_io_channel_unref (chan);
423 fprintf(stderr, "File '%s' not be read.\n", path);
430 parseenv (char* string) {
431 extern char** environ;
432 gchar* tmpstr = NULL;
436 while (environ[i] != NULL) {
437 gchar** env = g_strsplit (environ[i], "=", 2);
438 gchar* envname = g_strconcat ("$", env[0], NULL);
440 if (g_strrstr (string, envname) != NULL) {
441 tmpstr = g_strdup(string);
443 string = str_replace(envname, env[1], tmpstr);
448 g_strfreev (env); // somebody said this breaks uzbl
456 setup_signal(int signr, sigfunc *shandler) {
457 struct sigaction nh, oh;
459 nh.sa_handler = shandler;
460 sigemptyset(&nh.sa_mask);
463 if(sigaction(signr, &nh, &oh) < 0)
471 if (uzbl.behave.fifo_dir)
472 unlink (uzbl.comm.fifo_path);
473 if (uzbl.behave.socket_dir)
474 unlink (uzbl.comm.socket_path);
476 g_free(uzbl.state.executable_path);
477 g_free(uzbl.state.keycmd);
478 g_hash_table_destroy(uzbl.bindings);
479 g_hash_table_destroy(uzbl.behave.commands);
480 g_scanner_destroy(uzbl.scan);
483 /* used for html_mode_timeout
484 * be sure to extend this function to use
485 * more timers if needed in other places
488 set_timeout(int seconds) {
490 memset(&t, 0, sizeof t);
492 t.it_value.tv_sec = seconds;
493 t.it_value.tv_usec = 0;
494 setitimer(ITIMER_REAL, &t, NULL);
497 /* --- SIGNAL HANDLER --- */
500 catch_sigterm(int s) {
506 catch_sigint(int s) {
516 set_var_value("mode", "0");
521 /* --- CALLBACKS --- */
524 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527 (void) navigation_action;
528 (void) policy_decision;
530 const gchar* uri = webkit_network_request_get_uri (request);
531 if (uzbl.state.verbose)
532 printf("New window requested -> %s \n", uri);
533 webkit_web_policy_decision_use(policy_decision);
538 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
543 /* If we can display it, let's display it... */
544 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
545 webkit_web_policy_decision_use (policy_decision);
549 /* ...everything we can't displayed is downloaded */
550 webkit_web_policy_decision_download (policy_decision);
555 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
559 if (uzbl.state.selected_url != NULL) {
560 if (uzbl.state.verbose)
561 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
562 new_window_load_uri(uzbl.state.selected_url);
564 if (uzbl.state.verbose)
565 printf("New web view -> %s\n","Nothing to open, exiting");
571 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
574 if (uzbl.behave.download_handler) {
575 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
576 if (uzbl.state.verbose)
577 printf("Download -> %s\n",uri);
578 /* if urls not escaped, we may have to escape and quote uri before this call */
579 run_handler(uzbl.behave.download_handler, uri);
584 /* scroll a bar in a given direction */
586 scroll (GtkAdjustment* bar, GArray *argv) {
590 gdouble page_size = gtk_adjustment_get_page_size(bar);
591 gdouble value = gtk_adjustment_get_value(bar);
592 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
595 value += page_size * amount * 0.01;
599 max_value = gtk_adjustment_get_upper(bar) - page_size;
601 if (value > max_value)
602 value = max_value; /* don't scroll past the end of the page */
604 gtk_adjustment_set_value (bar, value);
608 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
609 (void) page; (void) argv; (void) result;
610 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
614 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
615 (void) page; (void) argv; (void) result;
616 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
617 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
621 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
622 (void) page; (void) result;
623 scroll(uzbl.gui.bar_v, argv);
627 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
628 (void) page; (void) result;
629 scroll(uzbl.gui.bar_h, argv);
634 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
635 if(uzbl.state.verbose)
636 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
638 /* update geometry var with the actual geometry
639 this is necessary as some WMs don't seem to honour
640 the above setting and we don't want to end up with
641 wrong geometry information
648 if (!uzbl.behave.show_status) {
649 gtk_widget_hide(uzbl.gui.mainbar);
651 gtk_widget_show(uzbl.gui.mainbar);
657 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
662 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
666 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
671 if (uzbl.behave.show_status) {
672 gtk_widget_hide(uzbl.gui.mainbar);
674 gtk_widget_show(uzbl.gui.mainbar);
676 uzbl.behave.show_status = !uzbl.behave.show_status;
681 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
685 //Set selected_url state variable
686 g_free(uzbl.state.selected_url);
687 uzbl.state.selected_url = NULL;
689 uzbl.state.selected_url = g_strdup(link);
695 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
698 const gchar *title = webkit_web_view_get_title(web_view);
699 if (uzbl.gui.main_title)
700 g_free (uzbl.gui.main_title);
701 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
706 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
709 uzbl.gui.sbar.load_progress = progress;
711 g_free(uzbl.gui.sbar.progress_bar);
712 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
718 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
722 if (uzbl.behave.load_finish_handler)
723 run_handler(uzbl.behave.load_finish_handler, "");
726 void clear_keycmd() {
727 g_free(uzbl.state.keycmd);
728 uzbl.state.keycmd = g_strdup("");
732 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
736 uzbl.gui.sbar.load_progress = 0;
737 clear_keycmd(); // don't need old commands to remain on new page?
738 if (uzbl.behave.load_start_handler)
739 run_handler(uzbl.behave.load_start_handler, "");
743 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
746 g_free (uzbl.state.uri);
747 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
748 uzbl.state.uri = g_string_free (newuri, FALSE);
749 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
750 set_insert_mode(uzbl.behave.always_insert_mode);
753 if (uzbl.behave.load_commit_handler)
754 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
758 destroy_cb (GtkWidget* widget, gpointer data) {
766 if (uzbl.behave.history_handler) {
768 struct tm * timeinfo;
771 timeinfo = localtime ( &rawtime );
772 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
773 run_handler(uzbl.behave.history_handler, date);
778 /* VIEW funcs (little webkit wrappers) */
779 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
781 VIEWFUNC(reload_bypass_cache)
782 VIEWFUNC(stop_loading)
789 /* -- command to callback/function map for things we cannot attach to any signals */
790 struct {char *key; CommandInfo value;} cmdlist[] =
791 { /* key function no_split */
792 { "back", {view_go_back, 0} },
793 { "forward", {view_go_forward, 0} },
794 { "scroll_vert", {scroll_vert, 0} },
795 { "scroll_horz", {scroll_horz, 0} },
796 { "scroll_begin", {scroll_begin, 0} },
797 { "scroll_end", {scroll_end, 0} },
798 { "reload", {view_reload, 0}, },
799 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
800 { "stop", {view_stop_loading, 0}, },
801 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
802 { "zoom_out", {view_zoom_out, 0}, },
803 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
804 { "uri", {load_uri, TRUE} },
805 { "js", {run_js, TRUE} },
806 { "script", {run_external_js, 0} },
807 { "toggle_status", {toggle_status_cb, 0} },
808 { "spawn", {spawn, 0} },
809 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
810 { "sh", {spawn_sh, 0} },
811 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
812 { "exit", {close_uzbl, 0} },
813 { "search", {search_forward_text, TRUE} },
814 { "search_reverse", {search_reverse_text, TRUE} },
815 { "dehilight", {dehilight, 0} },
816 { "toggle_insert_mode", {toggle_insert_mode, 0} },
817 { "set", {set_var, TRUE} },
818 //{ "get", {get_var, TRUE} },
819 { "bind", {act_bind, TRUE} },
820 { "dump_config", {act_dump_config, 0} },
821 { "keycmd", {keycmd, TRUE} },
822 { "keycmd_nl", {keycmd_nl, TRUE} },
823 { "keycmd_bs", {keycmd_bs, 0} },
824 { "chain", {chain, 0} },
825 { "print", {print, TRUE} }
832 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
834 for (i = 0; i < LENGTH(cmdlist); i++)
835 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
838 /* -- CORE FUNCTIONS -- */
841 free_action(gpointer act) {
842 Action *action = (Action*)act;
843 g_free(action->name);
845 g_free(action->param);
850 new_action(const gchar *name, const gchar *param) {
851 Action *action = g_new(Action, 1);
853 action->name = g_strdup(name);
855 action->param = g_strdup(param);
857 action->param = NULL;
863 file_exists (const char * filename) {
864 return (access(filename, F_OK) == 0);
868 set_var(WebKitWebView *page, GArray *argv, GString *result) {
869 (void) page; (void) result;
870 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
871 if (split[0] != NULL) {
872 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
873 set_var_value(g_strstrip(split[0]), value);
880 print(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) result;
884 buf = expand(argv_idx(argv, 0), 0);
885 g_string_assign(result, buf);
890 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
891 (void) page; (void) result;
892 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
893 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
894 add_binding(g_strstrip(split[0]), value);
912 set_mode_indicator() {
913 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
914 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
919 set_mode_indicator();
924 set_insert_mode(gboolean mode) {
925 uzbl.behave.insert_mode = mode;
926 set_mode_indicator();
930 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
931 (void) page; (void) result;
933 if (argv_idx(argv, 0)) {
934 if (strcmp (argv_idx(argv, 0), "0") == 0) {
935 set_insert_mode(FALSE);
937 set_insert_mode(TRUE);
940 set_insert_mode( !uzbl.behave.insert_mode );
947 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
950 if (argv_idx(argv, 0)) {
951 GString* newuri = g_string_new (argv_idx(argv, 0));
952 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
953 run_js(web_view, argv, NULL);
956 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
957 g_string_prepend (newuri, "http://");
958 /* if we do handle cookies, ask our handler for them */
959 webkit_web_view_load_uri (web_view, newuri->str);
960 g_string_free (newuri, TRUE);
967 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
968 size_t argumentCount, const JSValueRef arguments[],
969 JSValueRef* exception) {
974 JSStringRef js_result_string;
975 GString *result = g_string_new("");
977 if (argumentCount >= 1) {
978 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
979 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
980 char ctl_line[arg_size];
981 JSStringGetUTF8CString(arg, ctl_line, arg_size);
983 parse_cmd_line(ctl_line, result);
985 JSStringRelease(arg);
987 js_result_string = JSStringCreateWithUTF8CString(result->str);
989 g_string_free(result, TRUE);
991 return JSValueMakeString(ctx, js_result_string);
994 JSStaticFunction js_static_functions[] = {
995 {"run", js_run_command, kJSPropertyAttributeNone},
1000 /* This function creates the class and its definition, only once */
1001 if (!uzbl.js.initialized) {
1002 /* it would be pretty cool to make this dynamic */
1003 uzbl.js.classdef = kJSClassDefinitionEmpty;
1004 uzbl.js.classdef.staticFunctions = js_static_functions;
1006 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1012 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1013 WebKitWebFrame *frame;
1014 JSGlobalContextRef context;
1015 JSObjectRef globalobject;
1016 JSStringRef var_name;
1018 JSStringRef js_script;
1019 JSValueRef js_result;
1020 JSStringRef js_result_string;
1021 size_t js_result_size;
1025 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1026 context = webkit_web_frame_get_global_context(frame);
1027 globalobject = JSContextGetGlobalObject(context);
1029 /* uzbl javascript namespace */
1030 var_name = JSStringCreateWithUTF8CString("Uzbl");
1031 JSObjectSetProperty(context, globalobject, var_name,
1032 JSObjectMake(context, uzbl.js.classref, NULL),
1033 kJSClassAttributeNone, NULL);
1035 /* evaluate the script and get return value*/
1036 js_script = JSStringCreateWithUTF8CString(script);
1037 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1038 if (js_result && !JSValueIsUndefined(context, js_result)) {
1039 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1040 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1042 if (js_result_size) {
1043 char js_result_utf8[js_result_size];
1044 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1045 g_string_assign(result, js_result_utf8);
1048 JSStringRelease(js_result_string);
1052 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1054 JSStringRelease(var_name);
1055 JSStringRelease(js_script);
1059 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1060 if (argv_idx(argv, 0))
1061 eval_js(web_view, argv_idx(argv, 0), result);
1065 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1067 if (argv_idx(argv, 0)) {
1068 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1073 while ((line = g_array_index(lines, gchar*, i))) {
1075 js = g_strdup (line);
1077 gchar* newjs = g_strconcat (js, line, NULL);
1084 if (uzbl.state.verbose)
1085 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1087 if (argv_idx (argv, 1)) {
1088 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1092 eval_js (web_view, js, result);
1094 g_array_free (lines, TRUE);
1099 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1100 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1101 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1102 webkit_web_view_unmark_text_matches (page);
1103 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1104 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1108 if (uzbl.state.searchtx) {
1109 if (uzbl.state.verbose)
1110 printf ("Searching: %s\n", uzbl.state.searchtx);
1111 webkit_web_view_set_highlight_text_matches (page, TRUE);
1112 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1117 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1119 search_text(page, argv, TRUE);
1123 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1125 search_text(page, argv, FALSE);
1129 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1130 (void) argv; (void) result;
1131 webkit_web_view_set_highlight_text_matches (page, FALSE);
1136 new_window_load_uri (const gchar * uri) {
1137 if (uzbl.behave.new_window) {
1138 GString *s = g_string_new ("");
1139 g_string_printf(s, "'%s'", uri);
1140 run_handler(uzbl.behave.new_window, s->str);
1143 GString* to_execute = g_string_new ("");
1144 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1146 for (i = 0; entries[i].long_name != NULL; i++) {
1147 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1148 gchar** str = (gchar**)entries[i].arg_data;
1150 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1154 if (uzbl.state.verbose)
1155 printf("\n%s\n", to_execute->str);
1156 g_spawn_command_line_async (to_execute->str, NULL);
1157 g_string_free (to_execute, TRUE);
1161 chain (WebKitWebView *page, GArray *argv, GString *result) {
1162 (void) page; (void) result;
1164 gchar **parts = NULL;
1166 while ((a = argv_idx(argv, i++))) {
1167 parts = g_strsplit (a, " ", 2);
1169 parse_command(parts[0], parts[1], result);
1175 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1179 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1185 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1189 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1195 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1200 int len = strlen(uzbl.state.keycmd);
1201 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1203 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1208 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1215 /* --Statusbar functions-- */
1217 build_progressbar_ascii(int percent) {
1218 int width=uzbl.gui.sbar.progress_w;
1221 GString *bar = g_string_new("");
1223 l = (double)percent*((double)width/100.);
1224 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1226 for(i=0; i<(int)l; i++)
1227 g_string_append(bar, uzbl.gui.sbar.progress_s);
1230 g_string_append(bar, uzbl.gui.sbar.progress_u);
1232 return g_string_free(bar, FALSE);
1234 /* --End Statusbar functions-- */
1237 sharg_append(GArray *a, const gchar *str) {
1238 const gchar *s = (str ? str : "");
1239 g_array_append_val(a, s);
1242 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1244 run_command (const gchar *command, const guint npre, const gchar **args,
1245 const gboolean sync, char **output_stdout) {
1246 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1249 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1250 gchar *pid = itos(getpid());
1251 gchar *xwin = itos(uzbl.xwin);
1253 sharg_append(a, command);
1254 for (i = 0; i < npre; i++) /* add n args before the default vars */
1255 sharg_append(a, args[i]);
1256 sharg_append(a, uzbl.state.config_file);
1257 sharg_append(a, pid);
1258 sharg_append(a, xwin);
1259 sharg_append(a, uzbl.comm.fifo_path);
1260 sharg_append(a, uzbl.comm.socket_path);
1261 sharg_append(a, uzbl.state.uri);
1262 sharg_append(a, uzbl.gui.main_title);
1264 for (i = npre; i < g_strv_length((gchar**)args); i++)
1265 sharg_append(a, args[i]);
1269 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1271 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1272 NULL, NULL, output_stdout, NULL, NULL, &err);
1273 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1274 NULL, NULL, NULL, &err);
1276 if (uzbl.state.verbose) {
1277 GString *s = g_string_new("spawned:");
1278 for (i = 0; i < (a->len); i++) {
1279 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1280 g_string_append_printf(s, " %s", qarg);
1283 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1284 printf("%s\n", s->str);
1285 g_string_free(s, TRUE);
1287 printf("Stdout: %s\n", *output_stdout);
1291 g_printerr("error on run_command: %s\n", err->message);
1296 g_array_free (a, TRUE);
1301 split_quoted(const gchar* src, const gboolean unquote) {
1302 /* split on unquoted space, return array of strings;
1303 remove a layer of quotes and backslashes if unquote */
1304 if (!src) return NULL;
1306 gboolean dq = FALSE;
1307 gboolean sq = FALSE;
1308 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1309 GString *s = g_string_new ("");
1313 for (p = src; *p != '\0'; p++) {
1314 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1315 else if (*p == '\\') { g_string_append_c(s, *p++);
1316 g_string_append_c(s, *p); }
1317 else if ((*p == '"') && unquote && !sq) dq = !dq;
1318 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1320 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1321 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1323 else if ((*p == ' ') && !dq && !sq) {
1324 dup = g_strdup(s->str);
1325 g_array_append_val(a, dup);
1326 g_string_truncate(s, 0);
1327 } else g_string_append_c(s, *p);
1329 dup = g_strdup(s->str);
1330 g_array_append_val(a, dup);
1331 ret = (gchar**)a->data;
1332 g_array_free (a, FALSE);
1333 g_string_free (s, TRUE);
1338 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1339 (void)web_view; (void)result;
1340 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1341 if (argv_idx(argv, 0))
1342 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1346 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1347 (void)web_view; (void)result;
1349 if (argv_idx(argv, 0))
1350 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1351 TRUE, &uzbl.comm.sync_stdout);
1355 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1356 (void)web_view; (void)result;
1357 if (!uzbl.behave.shell_cmd) {
1358 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1363 gchar *spacer = g_strdup("");
1364 g_array_insert_val(argv, 1, spacer);
1365 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1367 for (i = 1; i < g_strv_length(cmd); i++)
1368 g_array_prepend_val(argv, cmd[i]);
1370 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1376 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1377 (void)web_view; (void)result;
1378 if (!uzbl.behave.shell_cmd) {
1379 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1384 gchar *spacer = g_strdup("");
1385 g_array_insert_val(argv, 1, spacer);
1386 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1388 for (i = 1; i < g_strv_length(cmd); i++)
1389 g_array_prepend_val(argv, cmd[i]);
1391 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1392 TRUE, &uzbl.comm.sync_stdout);
1398 parse_command(const char *cmd, const char *param, GString *result) {
1401 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1403 gchar **par = split_quoted(param, TRUE);
1404 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1406 if (c->no_split) { /* don't split */
1407 sharg_append(a, param);
1409 for (i = 0; i < g_strv_length(par); i++)
1410 sharg_append(a, par[i]);
1413 if (result == NULL) {
1414 GString *result_print = g_string_new("");
1416 c->function(uzbl.gui.web_view, a, result_print);
1417 if (result_print->len)
1418 printf("%*s\n", result_print->len, result_print->str);
1420 g_string_free(result_print, TRUE);
1422 c->function(uzbl.gui.web_view, a, result);
1425 g_array_free (a, TRUE);
1428 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1435 if(*uzbl.net.proxy_url == ' '
1436 || uzbl.net.proxy_url == NULL) {
1437 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1438 (GType) SOUP_SESSION_PROXY_URI);
1441 suri = soup_uri_new(uzbl.net.proxy_url);
1442 g_object_set(G_OBJECT(uzbl.net.soup_session),
1443 SOUP_SESSION_PROXY_URI,
1445 soup_uri_free(suri);
1452 if(file_exists(uzbl.gui.icon)) {
1453 if (uzbl.gui.main_window)
1454 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1456 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1462 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1463 g_array_append_val (a, uzbl.state.uri);
1464 load_uri(uzbl.gui.web_view, a, NULL);
1465 g_array_free (a, TRUE);
1469 cmd_always_insert_mode() {
1470 set_insert_mode(uzbl.behave.always_insert_mode);
1476 g_object_set(G_OBJECT(uzbl.net.soup_session),
1477 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1481 cmd_max_conns_host() {
1482 g_object_set(G_OBJECT(uzbl.net.soup_session),
1483 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1488 soup_session_remove_feature
1489 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1490 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1491 /*g_free(uzbl.net.soup_logger);*/
1493 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1494 soup_session_add_feature(uzbl.net.soup_session,
1495 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1500 return webkit_web_view_get_settings(uzbl.gui.web_view);
1505 WebKitWebSettings *ws = view_settings();
1506 if (uzbl.behave.font_size > 0) {
1507 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1510 if (uzbl.behave.monospace_size > 0) {
1511 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1512 uzbl.behave.monospace_size, NULL);
1514 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1515 uzbl.behave.font_size, NULL);
1521 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1525 cmd_disable_plugins() {
1526 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1527 !uzbl.behave.disable_plugins, NULL);
1531 cmd_disable_scripts() {
1532 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1533 !uzbl.behave.disable_scripts, NULL);
1537 cmd_minimum_font_size() {
1538 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1539 uzbl.behave.minimum_font_size, NULL);
1542 cmd_autoload_img() {
1543 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1544 uzbl.behave.autoload_img, NULL);
1549 cmd_autoshrink_img() {
1550 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1551 uzbl.behave.autoshrink_img, NULL);
1556 cmd_enable_spellcheck() {
1557 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1558 uzbl.behave.enable_spellcheck, NULL);
1562 cmd_enable_private() {
1563 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1564 uzbl.behave.enable_private, NULL);
1569 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1570 uzbl.behave.print_bg, NULL);
1575 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1576 uzbl.behave.style_uri, NULL);
1580 cmd_resizable_txt() {
1581 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1582 uzbl.behave.resizable_txt, NULL);
1586 cmd_default_encoding() {
1587 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1588 uzbl.behave.default_encoding, NULL);
1592 cmd_enforce_96dpi() {
1593 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1594 uzbl.behave.enforce_96dpi, NULL);
1598 cmd_caret_browsing() {
1599 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1600 uzbl.behave.caret_browsing, NULL);
1604 cmd_cookie_handler() {
1605 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1606 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1607 if ((g_strcmp0(split[0], "sh") == 0) ||
1608 (g_strcmp0(split[0], "spawn") == 0)) {
1609 g_free (uzbl.behave.cookie_handler);
1610 uzbl.behave.cookie_handler =
1611 g_strdup_printf("sync_%s %s", split[0], split[1]);
1618 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1619 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1620 if ((g_strcmp0(split[0], "sh") == 0) ||
1621 (g_strcmp0(split[0], "spawn") == 0)) {
1622 g_free (uzbl.behave.new_window);
1623 uzbl.behave.new_window =
1624 g_strdup_printf("%s %s", split[0], split[1]);
1631 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1636 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1641 if(uzbl.behave.inject_html) {
1642 webkit_web_view_load_html_string (uzbl.gui.web_view,
1643 uzbl.behave.inject_html, NULL);
1652 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1653 uzbl.behave.modmask = 0;
1655 if(uzbl.behave.modkey)
1656 g_free(uzbl.behave.modkey);
1657 uzbl.behave.modkey = buf;
1659 for (i = 0; modkeys[i].key != NULL; i++) {
1660 if (g_strrstr(buf, modkeys[i].key))
1661 uzbl.behave.modmask |= modkeys[i].mask;
1667 if (*uzbl.net.useragent == ' ') {
1668 g_free (uzbl.net.useragent);
1669 uzbl.net.useragent = NULL;
1671 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1672 uzbl.net.useragent, NULL);
1678 gtk_widget_ref(uzbl.gui.scrolled_win);
1679 gtk_widget_ref(uzbl.gui.mainbar);
1680 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1681 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1683 if(uzbl.behave.status_top) {
1684 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1685 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1688 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1689 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1691 gtk_widget_unref(uzbl.gui.scrolled_win);
1692 gtk_widget_unref(uzbl.gui.mainbar);
1693 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1698 set_var_value(gchar *name, gchar *val) {
1699 uzbl_cmdprop *c = NULL;
1703 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1704 if(!c->writeable) return FALSE;
1706 /* check for the variable type */
1707 if (c->type == TYPE_STR) {
1708 buf = expand(val, 0);
1711 } else if(c->type == TYPE_INT) {
1712 int *ip = (int *)c->ptr;
1713 buf = expand(val, 0);
1714 *ip = (int)strtoul(buf, &endp, 10);
1716 } else if (c->type == TYPE_FLOAT) {
1717 float *fp = (float *)c->ptr;
1718 buf = expand(val, 0);
1719 *fp = strtod(buf, &endp);
1723 /* invoke a command specific function */
1724 if(c->func) c->func();
1731 Behaviour *b = &uzbl.behave;
1733 if(b->html_buffer->str) {
1734 webkit_web_view_load_html_string (uzbl.gui.web_view,
1735 b->html_buffer->str, b->base_url);
1736 g_string_free(b->html_buffer, TRUE);
1737 b->html_buffer = g_string_new("");
1741 enum {M_CMD, M_HTML};
1743 parse_cmd_line(const char *ctl_line, GString *result) {
1744 Behaviour *b = &uzbl.behave;
1747 if(b->mode == M_HTML) {
1748 len = strlen(b->html_endmarker);
1749 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1750 if(len == strlen(ctl_line)-1 &&
1751 !strncmp(b->html_endmarker, ctl_line, len)) {
1753 set_var_value("mode", "0");
1758 set_timeout(b->html_timeout);
1759 g_string_append(b->html_buffer, ctl_line);
1762 else if((ctl_line[0] == '#') /* Comments */
1763 || (ctl_line[0] == ' ')
1764 || (ctl_line[0] == '\n'))
1765 ; /* ignore these lines */
1766 else { /* parse a command */
1768 gchar **tokens = NULL;
1769 len = strlen(ctl_line);
1771 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1772 ctlstrip = g_strndup(ctl_line, len - 1);
1773 else ctlstrip = g_strdup(ctl_line);
1775 tokens = g_strsplit(ctlstrip, " ", 2);
1776 parse_command(tokens[0], tokens[1], result);
1783 build_stream_name(int type, const gchar* dir) {
1784 State *s = &uzbl.state;
1788 str = g_strdup_printf
1789 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1790 } else if (type == SOCKET) {
1791 str = g_strdup_printf
1792 ("%s/uzbl_socket_%s", dir, s->instance_name);
1798 control_fifo(GIOChannel *gio, GIOCondition condition) {
1799 if (uzbl.state.verbose)
1800 printf("triggered\n");
1805 if (condition & G_IO_HUP)
1806 g_error ("Fifo: Read end of pipe died!\n");
1809 g_error ("Fifo: GIOChannel broke\n");
1811 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1812 if (ret == G_IO_STATUS_ERROR) {
1813 g_error ("Fifo: Error reading: %s\n", err->message);
1817 parse_cmd_line(ctl_line, NULL);
1824 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1825 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1826 if (unlink(uzbl.comm.fifo_path) == -1)
1827 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1828 g_free(uzbl.comm.fifo_path);
1829 uzbl.comm.fifo_path = NULL;
1832 GIOChannel *chan = NULL;
1833 GError *error = NULL;
1834 gchar *path = build_stream_name(FIFO, dir);
1836 if (!file_exists(path)) {
1837 if (mkfifo (path, 0666) == 0) {
1838 // 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.
1839 chan = g_io_channel_new_file(path, "r+", &error);
1841 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1842 if (uzbl.state.verbose)
1843 printf ("init_fifo: created successfully as %s\n", path);
1844 uzbl.comm.fifo_path = path;
1846 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1847 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1848 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1849 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1851 /* if we got this far, there was an error; cleanup */
1852 if (error) g_error_free (error);
1859 control_stdin(GIOChannel *gio, GIOCondition condition) {
1861 gchar *ctl_line = NULL;
1864 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1865 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1868 parse_cmd_line(ctl_line, NULL);
1876 GIOChannel *chan = NULL;
1877 GError *error = NULL;
1879 chan = g_io_channel_unix_new(fileno(stdin));
1881 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1882 g_error ("Stdin: could not add watch\n");
1884 if (uzbl.state.verbose)
1885 printf ("Stdin: watch added successfully\n");
1888 g_error ("Stdin: Error while opening: %s\n", error->message);
1890 if (error) g_error_free (error);
1894 control_socket(GIOChannel *chan) {
1895 struct sockaddr_un remote;
1896 unsigned int t = sizeof(remote);
1898 GIOChannel *clientchan;
1900 clientsock = accept (g_io_channel_unix_get_fd(chan),
1901 (struct sockaddr *) &remote, &t);
1903 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1904 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1905 (GIOFunc) control_client_socket, clientchan);
1912 control_client_socket(GIOChannel *clientchan) {
1914 GString *result = g_string_new("");
1915 GError *error = NULL;
1919 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1920 if (ret == G_IO_STATUS_ERROR) {
1921 g_warning ("Error reading: %s\n", error->message);
1922 g_io_channel_shutdown(clientchan, TRUE, &error);
1924 } else if (ret == G_IO_STATUS_EOF) {
1925 /* shutdown and remove channel watch from main loop */
1926 g_io_channel_shutdown(clientchan, TRUE, &error);
1931 parse_cmd_line (ctl_line, result);
1932 g_string_append_c(result, '\n');
1933 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1935 if (ret == G_IO_STATUS_ERROR) {
1936 g_warning ("Error writing: %s", error->message);
1938 g_io_channel_flush(clientchan, &error);
1941 if (error) g_error_free (error);
1942 g_string_free(result, TRUE);
1948 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1949 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1950 if (unlink(uzbl.comm.socket_path) == -1)
1951 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1952 g_free(uzbl.comm.socket_path);
1953 uzbl.comm.socket_path = NULL;
1961 GIOChannel *chan = NULL;
1963 struct sockaddr_un local;
1964 gchar *path = build_stream_name(SOCKET, dir);
1966 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1968 local.sun_family = AF_UNIX;
1969 strcpy (local.sun_path, path);
1970 unlink (local.sun_path);
1972 len = strlen (local.sun_path) + sizeof (local.sun_family);
1973 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1974 if (uzbl.state.verbose)
1975 printf ("init_socket: opened in %s\n", path);
1978 if( (chan = g_io_channel_unix_new(sock)) ) {
1979 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1980 uzbl.comm.socket_path = path;
1983 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1985 /* if we got this far, there was an error; cleanup */
1992 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1993 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1995 // this function may be called very early when the templates are not set (yet), hence the checks
1997 update_title (void) {
1998 Behaviour *b = &uzbl.behave;
2001 if (b->show_status) {
2002 if (b->title_format_short) {
2003 parsed = expand(b->title_format_short, 0);
2004 if (uzbl.gui.main_window)
2005 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2008 if (b->status_format) {
2009 parsed = expand(b->status_format, 0);
2010 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2013 if (b->status_background) {
2015 gdk_color_parse (b->status_background, &color);
2016 //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)
2017 if (uzbl.gui.main_window)
2018 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2019 else if (uzbl.gui.plug)
2020 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2023 if (b->title_format_long) {
2024 parsed = expand(b->title_format_long, 0);
2025 if (uzbl.gui.main_window)
2026 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2033 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2037 retreive_geometry();
2042 key_press_cb (GtkWidget* window, GdkEventKey* event)
2044 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2048 if (event->type != GDK_KEY_PRESS ||
2049 event->keyval == GDK_Page_Up ||
2050 event->keyval == GDK_Page_Down ||
2051 event->keyval == GDK_Up ||
2052 event->keyval == GDK_Down ||
2053 event->keyval == GDK_Left ||
2054 event->keyval == GDK_Right ||
2055 event->keyval == GDK_Shift_L ||
2056 event->keyval == GDK_Shift_R)
2059 /* turn off insert mode (if always_insert_mode is not used) */
2060 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2061 set_insert_mode(uzbl.behave.always_insert_mode);
2066 if (uzbl.behave.insert_mode &&
2067 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2068 (!uzbl.behave.modmask)
2073 if (event->keyval == GDK_Escape) {
2076 dehilight(uzbl.gui.web_view, NULL, NULL);
2080 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2081 if (event->keyval == GDK_Insert) {
2083 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2084 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2086 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2089 GString* keycmd = g_string_new(uzbl.state.keycmd);
2090 g_string_append (keycmd, str);
2091 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2098 if (event->keyval == GDK_BackSpace)
2099 keycmd_bs(NULL, NULL, NULL);
2101 gboolean key_ret = FALSE;
2102 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2105 GString* keycmd = g_string_new(uzbl.state.keycmd);
2106 g_string_append(keycmd, event->string);
2107 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2110 run_keycmd(key_ret);
2112 if (key_ret) return (!uzbl.behave.insert_mode);
2117 run_keycmd(const gboolean key_ret) {
2118 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2120 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2122 parse_command(act->name, act->param, NULL);
2126 /* try if it's an incremental keycmd or one that takes args, and run it */
2127 GString* short_keys = g_string_new ("");
2128 GString* short_keys_inc = g_string_new ("");
2130 guint len = strlen(uzbl.state.keycmd);
2131 for (i=0; i<len; i++) {
2132 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2133 g_string_assign(short_keys_inc, short_keys->str);
2134 g_string_append_c(short_keys, '_');
2135 g_string_append_c(short_keys_inc, '*');
2137 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2138 /* run normal cmds only if return was pressed */
2139 exec_paramcmd(act, i);
2142 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2143 if (key_ret) /* just quit the incremental command on return */
2145 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2149 g_string_truncate(short_keys, short_keys->len - 1);
2151 g_string_free (short_keys, TRUE);
2152 g_string_free (short_keys_inc, TRUE);
2156 exec_paramcmd(const Action *act, const guint i) {
2157 GString *parampart = g_string_new (uzbl.state.keycmd);
2158 GString *actionname = g_string_new ("");
2159 GString *actionparam = g_string_new ("");
2160 g_string_erase (parampart, 0, i+1);
2162 g_string_printf (actionname, act->name, parampart->str);
2164 g_string_printf (actionparam, act->param, parampart->str);
2165 parse_command(actionname->str, actionparam->str, NULL);
2166 g_string_free(actionname, TRUE);
2167 g_string_free(actionparam, TRUE);
2168 g_string_free(parampart, TRUE);
2176 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2177 //main_window_ref = g_object_ref(scrolled_window);
2178 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
2180 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2181 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2183 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2184 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2189 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2190 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2191 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2192 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2193 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2195 return scrolled_window;
2202 g->mainbar = gtk_hbox_new (FALSE, 0);
2204 /* keep a reference to the bar so we can re-pack it at runtime*/
2205 //sbar_ref = g_object_ref(g->mainbar);
2207 g->mainbar_label = gtk_label_new ("");
2208 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2209 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2210 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2211 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2212 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2213 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2219 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2220 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2221 gtk_widget_set_name (window, "Uzbl browser");
2222 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2223 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2224 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2231 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2232 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2233 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2240 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2242 If actname is one that calls an external command, this function will inject
2243 newargs in front of the user-provided args in that command line. They will
2244 come become after the body of the script (in sh) or after the name of
2245 the command to execute (in spawn).
2246 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2247 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2249 The return value consist of two strings: the action (sh, ...) and its args.
2251 If act is not one that calls an external command, then the given action merely
2254 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2255 /* Arrr! Here be memory leaks */
2256 gchar *actdup = g_strdup(actname);
2257 g_array_append_val(rets, actdup);
2259 if ((g_strcmp0(actname, "spawn") == 0) ||
2260 (g_strcmp0(actname, "sh") == 0) ||
2261 (g_strcmp0(actname, "sync_spawn") == 0) ||
2262 (g_strcmp0(actname, "sync_sh") == 0)) {
2264 GString *a = g_string_new("");
2265 gchar **spawnparts = split_quoted(origargs, FALSE);
2266 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2267 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2269 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2270 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2272 g_array_append_val(rets, a->str);
2273 g_string_free(a, FALSE);
2274 g_strfreev(spawnparts);
2276 gchar *origdup = g_strdup(origargs);
2277 g_array_append_val(rets, origdup);
2279 return (gchar**)g_array_free(rets, FALSE);
2283 run_handler (const gchar *act, const gchar *args) {
2284 /* Consider this code a temporary hack to make the handlers usable.
2285 In practice, all this splicing, injection, and reconstruction is
2286 inefficient, annoying and hard to manage. Potential pitfalls arise
2287 when the handler specific args 1) are not quoted (the handler
2288 callbacks should take care of this) 2) are quoted but interfere
2289 with the users' own quotation. A more ideal solution is
2290 to refactor parse_command so that it doesn't just take a string
2291 and execute it; rather than that, we should have a function which
2292 returns the argument vector parsed from the string. This vector
2293 could be modified (e.g. insert additional args into it) before
2294 passing it to the next function that actually executes it. Though
2295 it still isn't perfect for chain actions.. will reconsider & re-
2296 factor when I have the time. -duc */
2298 char **parts = g_strsplit(act, " ", 2);
2300 if (g_strcmp0(parts[0], "chain") == 0) {
2301 GString *newargs = g_string_new("");
2302 gchar **chainparts = split_quoted(parts[1], FALSE);
2304 /* for every argument in the chain, inject the handler args
2305 and make sure the new parts are wrapped in quotes */
2306 gchar **cp = chainparts;
2308 gchar *quotless = NULL;
2309 gchar **spliced_quotless = NULL; // sigh -_-;
2310 gchar **inpart = NULL;
2313 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2315 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2316 } else quotless = g_strdup(*cp);
2318 spliced_quotless = g_strsplit(quotless, " ", 2);
2319 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2320 g_strfreev(spliced_quotless);
2322 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2328 parse_command(parts[0], &(newargs->str[1]), NULL);
2329 g_string_free(newargs, TRUE);
2330 g_strfreev(chainparts);
2333 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2334 parse_command(inparts[0], inparts[1], NULL);
2342 add_binding (const gchar *key, const gchar *act) {
2343 char **parts = g_strsplit(act, " ", 2);
2350 if (uzbl.state.verbose)
2351 printf ("Binding %-10s : %s\n", key, act);
2352 action = new_action(parts[0], parts[1]);
2354 if (g_hash_table_remove (uzbl.bindings, key))
2355 g_warning ("Overwriting existing binding for \"%s\"", key);
2356 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2361 get_xdg_var (XDG_Var xdg) {
2362 const gchar* actual_value = getenv (xdg.environmental);
2363 const gchar* home = getenv ("HOME");
2364 gchar* return_value;
2366 if (! actual_value || strcmp (actual_value, "") == 0) {
2367 if (xdg.default_value) {
2368 return_value = str_replace ("~", home, xdg.default_value);
2370 return_value = NULL;
2373 return_value = str_replace("~", home, actual_value);
2376 return return_value;
2380 find_xdg_file (int xdg_type, char* filename) {
2381 /* xdg_type = 0 => config
2382 xdg_type = 1 => data
2383 xdg_type = 2 => cache*/
2385 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2386 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2389 gchar* temporary_string;
2393 if (! file_exists (temporary_file) && xdg_type != 2) {
2394 buf = get_xdg_var (XDG[3 + xdg_type]);
2395 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2398 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2399 g_free (temporary_file);
2400 temporary_file = g_strconcat (temporary_string, filename, NULL);
2404 //g_free (temporary_string); - segfaults.
2406 if (file_exists (temporary_file)) {
2407 return temporary_file;
2414 State *s = &uzbl.state;
2415 Network *n = &uzbl.net;
2417 for (i = 0; default_config[i].command != NULL; i++) {
2418 parse_cmd_line(default_config[i].command, NULL);
2421 if (g_strcmp0(s->config_file, "-") == 0) {
2422 s->config_file = NULL;
2426 else if (!s->config_file) {
2427 s->config_file = find_xdg_file (0, "/uzbl/config");
2430 if (s->config_file) {
2431 GArray* lines = read_file_by_line (s->config_file);
2435 while ((line = g_array_index(lines, gchar*, i))) {
2436 parse_cmd_line (line, NULL);
2440 g_array_free (lines, TRUE);
2442 if (uzbl.state.verbose)
2443 printf ("No configuration file loaded.\n");
2446 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2449 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2452 if (!uzbl.behave.cookie_handler)
2455 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2456 GString *s = g_string_new ("");
2457 SoupURI * soup_uri = soup_message_get_uri(msg);
2458 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2459 run_handler(uzbl.behave.cookie_handler, s->str);
2461 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2462 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2463 if ( p != NULL ) *p = '\0';
2464 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2466 if (uzbl.comm.sync_stdout)
2467 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2469 g_string_free(s, TRUE);
2473 save_cookies (SoupMessage *msg, gpointer user_data){
2477 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2478 cookie = soup_cookie_to_set_cookie_header(ck->data);
2479 SoupURI * soup_uri = soup_message_get_uri(msg);
2480 GString *s = g_string_new ("");
2481 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2482 run_handler(uzbl.behave.cookie_handler, s->str);
2484 g_string_free(s, TRUE);
2489 /* --- WEBINSPECTOR --- */
2491 hide_window_cb(GtkWidget *widget, gpointer data) {
2494 gtk_widget_hide(widget);
2498 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2501 (void) web_inspector;
2502 GtkWidget* scrolled_window;
2503 GtkWidget* new_web_view;
2506 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2507 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2508 G_CALLBACK(hide_window_cb), NULL);
2510 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2511 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2512 gtk_widget_show(g->inspector_window);
2514 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2515 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2516 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2517 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2518 gtk_widget_show(scrolled_window);
2520 new_web_view = webkit_web_view_new();
2521 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2523 return WEBKIT_WEB_VIEW(new_web_view);
2527 inspector_show_window_cb (WebKitWebInspector* inspector){
2529 gtk_widget_show(uzbl.gui.inspector_window);
2533 /* TODO: Add variables and code to make use of these functions */
2535 inspector_close_window_cb (WebKitWebInspector* inspector){
2541 inspector_attach_window_cb (WebKitWebInspector* inspector){
2547 inspector_detach_window_cb (WebKitWebInspector* inspector){
2553 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2559 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2565 set_up_inspector() {
2567 WebKitWebSettings *settings = view_settings();
2568 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2570 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2571 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2573 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2574 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2575 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2576 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2578 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2582 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2584 uzbl_cmdprop *c = v;
2589 if(c->type == TYPE_STR)
2590 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2591 else if(c->type == TYPE_INT)
2592 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2593 else if(c->type == TYPE_FLOAT)
2594 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2598 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2602 printf("bind %s = %s %s\n", (char *)k ,
2603 (char *)a->name, a->param?(char *)a->param:"");
2608 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2609 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2613 retreive_geometry() {
2615 GString *buf = g_string_new("");
2617 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2618 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2620 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2622 if(uzbl.gui.geometry)
2623 g_free(uzbl.gui.geometry);
2624 uzbl.gui.geometry = g_string_free(buf, FALSE);
2627 /* set up gtk, gobject, variable defaults and other things that tests and other
2628 * external applications need to do anyhow */
2630 initialize(int argc, char *argv[]) {
2631 gtk_init (&argc, &argv);
2632 if (!g_thread_supported ())
2633 g_thread_init (NULL);
2634 uzbl.state.executable_path = g_strdup(argv[0]);
2635 uzbl.state.selected_url = NULL;
2636 uzbl.state.searchtx = NULL;
2638 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2639 g_option_context_add_main_entries (context, entries, NULL);
2640 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2641 g_option_context_parse (context, &argc, &argv, NULL);
2642 g_option_context_free(context);
2644 if (uzbl.behave.print_version) {
2645 printf("Commit: %s\n", COMMIT);
2649 /* initialize hash table */
2650 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2652 uzbl.net.soup_session = webkit_get_default_session();
2653 uzbl.state.keycmd = g_strdup("");
2655 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2656 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2657 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2658 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2659 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2660 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2662 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2663 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2664 uzbl.gui.sbar.progress_w = 10;
2666 /* HTML mode defaults*/
2667 uzbl.behave.html_buffer = g_string_new("");
2668 uzbl.behave.html_endmarker = g_strdup(".");
2669 uzbl.behave.html_timeout = 60;
2670 uzbl.behave.base_url = g_strdup("http://invalid");
2672 /* default mode indicators */
2673 uzbl.behave.insert_indicator = g_strdup("I");
2674 uzbl.behave.cmd_indicator = g_strdup("C");
2676 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2677 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2678 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2679 uzbl.info.arch = ARCH;
2680 uzbl.info.commit = COMMIT;
2683 make_var_to_name_hash();
2685 uzbl.gui.scrolled_win = create_browser();
2688 #ifndef UZBL_LIBRARY
2691 main (int argc, char* argv[]) {
2692 initialize(argc, argv);
2694 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2698 /* initial packing */
2699 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2700 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2702 if (uzbl.state.socket_id) {
2703 uzbl.gui.plug = create_plug ();
2704 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2705 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2707 uzbl.gui.main_window = create_window ();
2708 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2709 gtk_widget_show_all (uzbl.gui.main_window);
2710 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2713 if(!uzbl.state.instance_name)
2714 uzbl.state.instance_name = itos((int)uzbl.xwin);
2716 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2718 if (uzbl.state.verbose) {
2719 printf("Uzbl start location: %s\n", argv[0]);
2720 if (uzbl.state.socket_id)
2721 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2723 printf("window_id %i\n",(int) uzbl.xwin);
2724 printf("pid %i\n", getpid ());
2725 printf("name: %s\n", uzbl.state.instance_name);
2728 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2729 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2730 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2731 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2732 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2734 if(uzbl.gui.geometry)
2737 retreive_geometry();
2739 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2740 gboolean verbose_override = uzbl.state.verbose;
2743 set_insert_mode(FALSE);
2745 if (!uzbl.behave.show_status)
2746 gtk_widget_hide(uzbl.gui.mainbar);
2753 if (verbose_override > uzbl.state.verbose)
2754 uzbl.state.verbose = verbose_override;
2757 set_var_value("uri", uri_override);
2758 g_free(uri_override);
2759 } else if (uzbl.state.uri)
2760 cmd_load_uri(uzbl.gui.web_view, NULL);
2765 return EXIT_SUCCESS;
2769 /* vi: set et ts=4: */