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>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
79 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
81 "Print the version and exit", NULL },
82 { NULL, 0, 0, 0, NULL, NULL, NULL }
85 /* associate command names to their properties */
86 typedef const struct {
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_title)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
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)},
146 /* exported WebKitWebSettings properties */
147 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
148 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
149 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
150 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
151 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
152 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
153 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
154 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
155 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
156 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
157 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
158 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
159 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
160 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
161 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
162 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
164 /* constants (not dumpable or writeable) */
165 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
166 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
167 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
168 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
169 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
170 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
171 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
172 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
173 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
174 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
175 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
177 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
178 }, *n2v_p = var_name_to_ptr;
185 { "SHIFT", GDK_SHIFT_MASK }, // shift
186 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
187 { "CONTROL", GDK_CONTROL_MASK }, // control
188 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
189 { "MOD2", GDK_MOD2_MASK }, // 5th mod
190 { "MOD3", GDK_MOD3_MASK }, // 6th mod
191 { "MOD4", GDK_MOD4_MASK }, // 7th mod
192 { "MOD5", GDK_MOD5_MASK }, // 8th mod
193 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
194 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
195 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
196 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
197 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
198 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
199 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
200 { "META", GDK_META_MASK }, // meta (since 2.10)
205 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
206 * 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 --- */
218 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
220 get_exp_type(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;
237 * recurse == 1: don't expand '@(command)@'
238 * recurse == 2: don't expand '@<java script>@'
241 expand(char *s, guint recurse) {
245 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
250 gchar *cmd_stdout = NULL;
252 GString *buf = g_string_new("");
253 GString *js_ret = g_string_new("");
258 g_string_append_c(buf, *++s);
263 etype = get_exp_type(s);
268 vend = strpbrk(s, end_simple_var);
269 if(!vend) vend = strchr(s, '\0');
273 vend = strchr(s, upto);
274 if(!vend) vend = strchr(s, '\0');
278 strcpy(str_end, ")@");
280 vend = strstr(s, str_end);
281 if(!vend) vend = strchr(s, '\0');
285 strcpy(str_end, ">@");
287 vend = strstr(s, str_end);
288 if(!vend) vend = strchr(s, '\0');
292 strcpy(str_end, "]@");
294 vend = strstr(s, str_end);
295 if(!vend) vend = strchr(s, '\0');
300 strncpy(ret, s, vend-s);
304 if(etype == EXP_SIMPLE_VAR ||
305 etype == EXP_BRACED_VAR) {
306 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
307 if(c->type == TYPE_STR && *c->ptr != NULL) {
308 g_string_append(buf, (gchar *)*c->ptr);
309 } else if(c->type == TYPE_INT) {
310 char *b = itos((uintptr_t)*c->ptr);
311 g_string_append(buf, b);
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 gchar* 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);
482 /* used for html_mode_timeout
483 * be sure to extend this function to use
484 * more timers if needed in other places
487 set_timeout(int seconds) {
489 memset(&t, 0, sizeof t);
491 t.it_value.tv_sec = seconds;
492 t.it_value.tv_usec = 0;
493 setitimer(ITIMER_REAL, &t, NULL);
496 /* --- SIGNAL HANDLER --- */
499 catch_sigterm(int s) {
505 catch_sigint(int s) {
515 set_var_value("mode", "0");
520 /* --- CALLBACKS --- */
523 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
526 (void) navigation_action;
527 (void) policy_decision;
529 const gchar* uri = webkit_network_request_get_uri (request);
530 if (uzbl.state.verbose)
531 printf("New window requested -> %s \n", uri);
532 webkit_web_policy_decision_use(policy_decision);
537 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
542 /* If we can display it, let's display it... */
543 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
544 webkit_web_policy_decision_use (policy_decision);
548 /* ...everything we can't displayed is downloaded */
549 webkit_web_policy_decision_download (policy_decision);
554 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
558 if (uzbl.state.selected_url != NULL) {
559 if (uzbl.state.verbose)
560 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
561 new_window_load_uri(uzbl.state.selected_url);
563 if (uzbl.state.verbose)
564 printf("New web view -> %s\n","Nothing to open, exiting");
570 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
573 if (uzbl.behave.download_handler) {
574 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
575 if (uzbl.state.verbose)
576 printf("Download -> %s\n",uri);
577 /* if urls not escaped, we may have to escape and quote uri before this call */
578 run_handler(uzbl.behave.download_handler, uri);
583 /* scroll a bar in a given direction */
585 scroll (GtkAdjustment* bar, GArray *argv) {
589 gdouble page_size = gtk_adjustment_get_page_size(bar);
590 gdouble value = gtk_adjustment_get_value(bar);
591 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
594 value += page_size * amount * 0.01;
598 max_value = gtk_adjustment_get_upper(bar) - page_size;
600 if (value > max_value)
601 value = max_value; /* don't scroll past the end of the page */
603 gtk_adjustment_set_value (bar, value);
607 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
608 (void) page; (void) argv; (void) result;
609 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
613 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
614 (void) page; (void) argv; (void) result;
615 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
616 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
620 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
621 (void) page; (void) result;
622 scroll(uzbl.gui.bar_v, argv);
626 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
627 (void) page; (void) result;
628 scroll(uzbl.gui.bar_h, argv);
633 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
634 if(uzbl.state.verbose)
635 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
637 /* update geometry var with the actual geometry
638 this is necessary as some WMs don't seem to honour
639 the above setting and we don't want to end up with
640 wrong geometry information
647 if (!uzbl.behave.show_status) {
648 gtk_widget_hide(uzbl.gui.mainbar);
650 gtk_widget_show(uzbl.gui.mainbar);
656 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
661 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
665 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
670 if (uzbl.behave.show_status) {
671 gtk_widget_hide(uzbl.gui.mainbar);
673 gtk_widget_show(uzbl.gui.mainbar);
675 uzbl.behave.show_status = !uzbl.behave.show_status;
680 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
684 //Set selected_url state variable
685 g_free(uzbl.state.selected_url);
686 uzbl.state.selected_url = NULL;
688 uzbl.state.selected_url = g_strdup(link);
694 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
697 const gchar *title = webkit_web_view_get_title(web_view);
698 if (uzbl.gui.main_title)
699 g_free (uzbl.gui.main_title);
700 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
705 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
708 uzbl.gui.sbar.load_progress = progress;
710 g_free(uzbl.gui.sbar.progress_bar);
711 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
717 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
721 if (uzbl.behave.load_finish_handler)
722 run_handler(uzbl.behave.load_finish_handler, "");
725 void clear_keycmd() {
726 g_free(uzbl.state.keycmd);
727 uzbl.state.keycmd = g_strdup("");
731 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
735 uzbl.gui.sbar.load_progress = 0;
736 clear_keycmd(); // don't need old commands to remain on new page?
737 if (uzbl.behave.load_start_handler)
738 run_handler(uzbl.behave.load_start_handler, "");
742 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
745 g_free (uzbl.state.uri);
746 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
747 uzbl.state.uri = g_string_free (newuri, FALSE);
748 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
749 set_insert_mode(uzbl.behave.always_insert_mode);
752 if (uzbl.behave.load_commit_handler)
753 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
757 destroy_cb (GtkWidget* widget, gpointer data) {
765 if (uzbl.behave.history_handler) {
767 struct tm * timeinfo;
770 timeinfo = localtime ( &rawtime );
771 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
772 run_handler(uzbl.behave.history_handler, date);
777 /* VIEW funcs (little webkit wrappers) */
778 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
780 VIEWFUNC(reload_bypass_cache)
781 VIEWFUNC(stop_loading)
788 /* -- command to callback/function map for things we cannot attach to any signals */
789 static struct {char *key; CommandInfo value;} cmdlist[] =
790 { /* key function no_split */
791 { "back", {view_go_back, 0} },
792 { "forward", {view_go_forward, 0} },
793 { "scroll_vert", {scroll_vert, 0} },
794 { "scroll_horz", {scroll_horz, 0} },
795 { "scroll_begin", {scroll_begin, 0} },
796 { "scroll_end", {scroll_end, 0} },
797 { "reload", {view_reload, 0}, },
798 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
799 { "stop", {view_stop_loading, 0}, },
800 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
801 { "zoom_out", {view_zoom_out, 0}, },
802 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
803 { "uri", {load_uri, TRUE} },
804 { "js", {run_js, TRUE} },
805 { "script", {run_external_js, 0} },
806 { "toggle_status", {toggle_status_cb, 0} },
807 { "spawn", {spawn, 0} },
808 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
809 { "sh", {spawn_sh, 0} },
810 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
811 { "exit", {close_uzbl, 0} },
812 { "search", {search_forward_text, TRUE} },
813 { "search_reverse", {search_reverse_text, TRUE} },
814 { "dehilight", {dehilight, 0} },
815 { "toggle_insert_mode", {toggle_insert_mode, 0} },
816 { "set", {set_var, TRUE} },
817 //{ "get", {get_var, TRUE} },
818 { "bind", {act_bind, TRUE} },
819 { "dump_config", {act_dump_config, 0} },
820 { "keycmd", {keycmd, TRUE} },
821 { "keycmd_nl", {keycmd_nl, TRUE} },
822 { "keycmd_bs", {keycmd_bs, 0} },
823 { "chain", {chain, 0} },
824 { "print", {print, TRUE} }
831 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
833 for (i = 0; i < LENGTH(cmdlist); i++)
834 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
837 /* -- CORE FUNCTIONS -- */
840 free_action(gpointer act) {
841 Action *action = (Action*)act;
842 g_free(action->name);
844 g_free(action->param);
849 new_action(const gchar *name, const gchar *param) {
850 Action *action = g_new(Action, 1);
852 action->name = g_strdup(name);
854 action->param = g_strdup(param);
856 action->param = NULL;
862 file_exists (const char * filename) {
863 return (access(filename, F_OK) == 0);
867 set_var(WebKitWebView *page, GArray *argv, GString *result) {
868 (void) page; (void) result;
869 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
870 if (split[0] != NULL) {
871 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
872 set_var_value(g_strstrip(split[0]), value);
879 print(WebKitWebView *page, GArray *argv, GString *result) {
880 (void) page; (void) result;
883 buf = expand(argv_idx(argv, 0), 0);
884 g_string_assign(result, buf);
889 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
890 (void) page; (void) result;
891 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
892 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
893 add_binding(g_strstrip(split[0]), value);
909 void set_mode_indicator() {
910 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
911 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
914 void set_insert_mode(gboolean mode) {
915 uzbl.behave.insert_mode = mode;
916 set_mode_indicator();
920 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
921 (void) page; (void) result;
923 if (argv_idx(argv, 0)) {
924 if (strcmp (argv_idx(argv, 0), "0") == 0) {
925 set_insert_mode(FALSE);
927 set_insert_mode(TRUE);
930 set_insert_mode( !uzbl.behave.insert_mode );
937 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
940 if (argv_idx(argv, 0)) {
941 GString* newuri = g_string_new (argv_idx(argv, 0));
942 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
943 run_js(web_view, argv, NULL);
946 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
947 g_string_prepend (newuri, "http://");
948 /* if we do handle cookies, ask our handler for them */
949 webkit_web_view_load_uri (web_view, newuri->str);
950 g_string_free (newuri, TRUE);
958 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
959 size_t argumentCount, const JSValueRef arguments[],
960 JSValueRef* exception) {
965 JSStringRef js_result_string;
966 GString *result = g_string_new("");
968 if (argumentCount >= 1) {
969 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
970 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
971 char ctl_line[arg_size];
972 JSStringGetUTF8CString(arg, ctl_line, arg_size);
974 parse_cmd_line(ctl_line, result);
976 JSStringRelease(arg);
978 js_result_string = JSStringCreateWithUTF8CString(result->str);
980 g_string_free(result, TRUE);
982 return JSValueMakeString(ctx, js_result_string);
985 static JSStaticFunction js_static_functions[] = {
986 {"run", js_run_command, kJSPropertyAttributeNone},
991 /* This function creates the class and its definition, only once */
992 if (!uzbl.js.initialized) {
993 /* it would be pretty cool to make this dynamic */
994 uzbl.js.classdef = kJSClassDefinitionEmpty;
995 uzbl.js.classdef.staticFunctions = js_static_functions;
997 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1003 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1004 WebKitWebFrame *frame;
1005 JSGlobalContextRef context;
1006 JSObjectRef globalobject;
1007 JSStringRef var_name;
1009 JSStringRef js_script;
1010 JSValueRef js_result;
1011 JSStringRef js_result_string;
1012 size_t js_result_size;
1016 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1017 context = webkit_web_frame_get_global_context(frame);
1018 globalobject = JSContextGetGlobalObject(context);
1020 /* uzbl javascript namespace */
1021 var_name = JSStringCreateWithUTF8CString("Uzbl");
1022 JSObjectSetProperty(context, globalobject, var_name,
1023 JSObjectMake(context, uzbl.js.classref, NULL),
1024 kJSClassAttributeNone, NULL);
1026 /* evaluate the script and get return value*/
1027 js_script = JSStringCreateWithUTF8CString(script);
1028 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1029 if (js_result && !JSValueIsUndefined(context, js_result)) {
1030 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1031 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1033 if (js_result_size) {
1034 char js_result_utf8[js_result_size];
1035 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1036 g_string_assign(result, js_result_utf8);
1039 JSStringRelease(js_result_string);
1043 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1045 JSStringRelease(var_name);
1046 JSStringRelease(js_script);
1050 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1052 if (argv_idx(argv, 0))
1053 eval_js(web_view, argv_idx(argv, 0), result);
1057 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1059 if (argv_idx(argv, 0)) {
1060 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1065 while ((line = g_array_index(lines, gchar*, i))) {
1067 js = g_strdup (line);
1069 gchar* newjs = g_strconcat (js, line, NULL);
1076 if (uzbl.state.verbose)
1077 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1079 if (argv_idx (argv, 1)) {
1080 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1084 eval_js (web_view, js, result);
1086 g_array_free (lines, TRUE);
1091 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1092 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1093 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1094 webkit_web_view_unmark_text_matches (page);
1095 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1096 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1100 if (uzbl.state.searchtx) {
1101 if (uzbl.state.verbose)
1102 printf ("Searching: %s\n", uzbl.state.searchtx);
1103 webkit_web_view_set_highlight_text_matches (page, TRUE);
1104 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1109 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1111 search_text(page, argv, TRUE);
1115 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1117 search_text(page, argv, FALSE);
1121 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1122 (void) argv; (void) result;
1123 webkit_web_view_set_highlight_text_matches (page, FALSE);
1128 new_window_load_uri (const gchar * uri) {
1129 if (uzbl.behave.new_window) {
1130 GString *s = g_string_new ("");
1131 g_string_printf(s, "'%s'", uri);
1132 run_handler(uzbl.behave.new_window, s->str);
1135 GString* to_execute = g_string_new ("");
1136 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1138 for (i = 0; entries[i].long_name != NULL; i++) {
1139 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1140 gchar** str = (gchar**)entries[i].arg_data;
1142 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1146 if (uzbl.state.verbose)
1147 printf("\n%s\n", to_execute->str);
1148 g_spawn_command_line_async (to_execute->str, NULL);
1149 g_string_free (to_execute, TRUE);
1153 chain (WebKitWebView *page, GArray *argv, GString *result) {
1154 (void) page; (void) result;
1156 gchar **parts = NULL;
1158 while ((a = argv_idx(argv, i++))) {
1159 parts = g_strsplit (a, " ", 2);
1160 parse_command(parts[0], parts[1], result);
1166 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1170 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1176 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1180 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1186 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1191 int len = strlen(uzbl.state.keycmd);
1192 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1194 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1199 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1206 /* --Statusbar functions-- */
1208 build_progressbar_ascii(int percent) {
1209 int width=uzbl.gui.sbar.progress_w;
1212 GString *bar = g_string_new("");
1214 l = (double)percent*((double)width/100.);
1215 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1217 for(i=0; i<(int)l; i++)
1218 g_string_append(bar, uzbl.gui.sbar.progress_s);
1221 g_string_append(bar, uzbl.gui.sbar.progress_u);
1223 return g_string_free(bar, FALSE);
1225 /* --End Statusbar functions-- */
1228 sharg_append(GArray *a, const gchar *str) {
1229 const gchar *s = (str ? str : "");
1230 g_array_append_val(a, s);
1233 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1235 run_command (const gchar *command, const guint npre, const gchar **args,
1236 const gboolean sync, char **output_stdout) {
1237 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1240 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1241 gchar *pid = itos(getpid());
1242 gchar *xwin = itos(uzbl.xwin);
1244 sharg_append(a, command);
1245 for (i = 0; i < npre; i++) /* add n args before the default vars */
1246 sharg_append(a, args[i]);
1247 sharg_append(a, uzbl.state.config_file);
1248 sharg_append(a, pid);
1249 sharg_append(a, xwin);
1250 sharg_append(a, uzbl.comm.fifo_path);
1251 sharg_append(a, uzbl.comm.socket_path);
1252 sharg_append(a, uzbl.state.uri);
1253 sharg_append(a, uzbl.gui.main_title);
1255 for (i = npre; i < g_strv_length((gchar**)args); i++)
1256 sharg_append(a, args[i]);
1260 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1262 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1263 NULL, NULL, output_stdout, NULL, NULL, &err);
1264 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1265 NULL, NULL, NULL, &err);
1267 if (uzbl.state.verbose) {
1268 GString *s = g_string_new("spawned:");
1269 for (i = 0; i < (a->len); i++) {
1270 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1271 g_string_append_printf(s, " %s", qarg);
1274 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1275 printf("%s\n", s->str);
1276 g_string_free(s, TRUE);
1278 printf("Stdout: %s\n", *output_stdout);
1282 g_printerr("error on run_command: %s\n", err->message);
1287 g_array_free (a, TRUE);
1292 split_quoted(const gchar* src, const gboolean unquote) {
1293 /* split on unquoted space, return array of strings;
1294 remove a layer of quotes and backslashes if unquote */
1295 if (!src) return NULL;
1297 gboolean dq = FALSE;
1298 gboolean sq = FALSE;
1299 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1300 GString *s = g_string_new ("");
1304 for (p = src; *p != '\0'; p++) {
1305 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1306 else if (*p == '\\') { g_string_append_c(s, *p++);
1307 g_string_append_c(s, *p); }
1308 else if ((*p == '"') && unquote && !sq) dq = !dq;
1309 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1311 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1312 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1314 else if ((*p == ' ') && !dq && !sq) {
1315 dup = g_strdup(s->str);
1316 g_array_append_val(a, dup);
1317 g_string_truncate(s, 0);
1318 } else g_string_append_c(s, *p);
1320 dup = g_strdup(s->str);
1321 g_array_append_val(a, dup);
1322 ret = (gchar**)a->data;
1323 g_array_free (a, FALSE);
1324 g_string_free (s, TRUE);
1329 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1330 (void)web_view; (void)result;
1331 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1332 if (argv_idx(argv, 0))
1333 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1337 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1338 (void)web_view; (void)result;
1340 if (argv_idx(argv, 0))
1341 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1342 TRUE, &uzbl.comm.sync_stdout);
1346 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1347 (void)web_view; (void)result;
1348 if (!uzbl.behave.shell_cmd) {
1349 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1354 gchar *spacer = g_strdup("");
1355 g_array_insert_val(argv, 1, spacer);
1356 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1358 for (i = 1; i < g_strv_length(cmd); i++)
1359 g_array_prepend_val(argv, cmd[i]);
1361 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1367 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1368 (void)web_view; (void)result;
1369 if (!uzbl.behave.shell_cmd) {
1370 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1375 gchar *spacer = g_strdup("");
1376 g_array_insert_val(argv, 1, spacer);
1377 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1379 for (i = 1; i < g_strv_length(cmd); i++)
1380 g_array_prepend_val(argv, cmd[i]);
1382 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1383 TRUE, &uzbl.comm.sync_stdout);
1389 parse_command(const char *cmd, const char *param, GString *result) {
1392 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1394 gchar **par = split_quoted(param, TRUE);
1395 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1397 if (c->no_split) { /* don't split */
1398 sharg_append(a, param);
1400 for (i = 0; i < g_strv_length(par); i++)
1401 sharg_append(a, par[i]);
1404 if (result == NULL) {
1405 GString *result_print = g_string_new("");
1407 c->function(uzbl.gui.web_view, a, result_print);
1408 if (result_print->len)
1409 printf("%*s\n", result_print->len, result_print->str);
1411 g_string_free(result_print, TRUE);
1413 c->function(uzbl.gui.web_view, a, result);
1416 g_array_free (a, TRUE);
1419 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1426 if(*uzbl.net.proxy_url == ' '
1427 || uzbl.net.proxy_url == NULL) {
1428 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1429 (GType) SOUP_SESSION_PROXY_URI);
1432 suri = soup_uri_new(uzbl.net.proxy_url);
1433 g_object_set(G_OBJECT(uzbl.net.soup_session),
1434 SOUP_SESSION_PROXY_URI,
1436 soup_uri_free(suri);
1443 if(file_exists(uzbl.gui.icon)) {
1444 if (uzbl.gui.main_window)
1445 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1447 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1453 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1454 g_array_append_val (a, uzbl.state.uri);
1455 load_uri(uzbl.gui.web_view, a, NULL);
1456 g_array_free (a, TRUE);
1460 cmd_always_insert_mode() {
1461 set_insert_mode(uzbl.behave.always_insert_mode);
1467 g_object_set(G_OBJECT(uzbl.net.soup_session),
1468 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1472 cmd_max_conns_host() {
1473 g_object_set(G_OBJECT(uzbl.net.soup_session),
1474 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1479 soup_session_remove_feature
1480 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1481 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1482 /*g_free(uzbl.net.soup_logger);*/
1484 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1485 soup_session_add_feature(uzbl.net.soup_session,
1486 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1489 static WebKitWebSettings*
1491 return webkit_web_view_get_settings(uzbl.gui.web_view);
1496 WebKitWebSettings *ws = view_settings();
1497 if (uzbl.behave.font_size > 0) {
1498 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1501 if (uzbl.behave.monospace_size > 0) {
1502 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1503 uzbl.behave.monospace_size, NULL);
1505 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1506 uzbl.behave.font_size, NULL);
1512 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1516 cmd_disable_plugins() {
1517 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1518 !uzbl.behave.disable_plugins, NULL);
1522 cmd_disable_scripts() {
1523 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1524 !uzbl.behave.disable_scripts, NULL);
1528 cmd_minimum_font_size() {
1529 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1530 uzbl.behave.minimum_font_size, NULL);
1533 cmd_autoload_img() {
1534 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1535 uzbl.behave.autoload_img, NULL);
1540 cmd_autoshrink_img() {
1541 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1542 uzbl.behave.autoshrink_img, NULL);
1547 cmd_enable_spellcheck() {
1548 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1549 uzbl.behave.enable_spellcheck, NULL);
1553 cmd_enable_private() {
1554 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1555 uzbl.behave.enable_private, NULL);
1560 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1561 uzbl.behave.print_bg, NULL);
1566 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1567 uzbl.behave.style_uri, NULL);
1571 cmd_resizable_txt() {
1572 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1573 uzbl.behave.resizable_txt, NULL);
1577 cmd_default_encoding() {
1578 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1579 uzbl.behave.default_encoding, NULL);
1583 cmd_enforce_96dpi() {
1584 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1585 uzbl.behave.enforce_96dpi, NULL);
1589 cmd_caret_browsing() {
1590 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1591 uzbl.behave.caret_browsing, NULL);
1595 cmd_cookie_handler() {
1596 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1597 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1598 if ((g_strcmp0(split[0], "sh") == 0) ||
1599 (g_strcmp0(split[0], "spawn") == 0)) {
1600 g_free (uzbl.behave.cookie_handler);
1601 uzbl.behave.cookie_handler =
1602 g_strdup_printf("sync_%s %s", split[0], split[1]);
1609 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1610 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1611 if ((g_strcmp0(split[0], "sh") == 0) ||
1612 (g_strcmp0(split[0], "spawn") == 0)) {
1613 g_free (uzbl.behave.new_window);
1614 uzbl.behave.new_window =
1615 g_strdup_printf("%s %s", split[0], split[1]);
1622 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1627 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1632 if(uzbl.behave.inject_html) {
1633 webkit_web_view_load_html_string (uzbl.gui.web_view,
1634 uzbl.behave.inject_html, NULL);
1643 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1644 uzbl.behave.modmask = 0;
1646 if(uzbl.behave.modkey)
1647 g_free(uzbl.behave.modkey);
1648 uzbl.behave.modkey = buf;
1650 for (i = 0; modkeys[i].key != NULL; i++) {
1651 if (g_strrstr(buf, modkeys[i].key))
1652 uzbl.behave.modmask |= modkeys[i].mask;
1658 if (*uzbl.net.useragent == ' ') {
1659 g_free (uzbl.net.useragent);
1660 uzbl.net.useragent = NULL;
1662 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1663 uzbl.net.useragent, NULL);
1669 gtk_widget_ref(uzbl.gui.scrolled_win);
1670 gtk_widget_ref(uzbl.gui.mainbar);
1671 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1672 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1674 if(uzbl.behave.status_top) {
1675 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1676 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1679 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1680 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1682 gtk_widget_unref(uzbl.gui.scrolled_win);
1683 gtk_widget_unref(uzbl.gui.mainbar);
1684 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1689 set_var_value(gchar *name, gchar *val) {
1690 uzbl_cmdprop *c = NULL;
1694 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1695 if(!c->writeable) return TRUE;
1697 /* check for the variable type */
1698 if (c->type == TYPE_STR) {
1699 buf = expand(val, 0);
1702 } else if(c->type == TYPE_INT) {
1703 int *ip = (int *)c->ptr;
1704 buf = expand(val, 0);
1705 *ip = (int)strtoul(buf, &endp, 10);
1707 } else if (c->type == TYPE_FLOAT) {
1708 float *fp = (float *)c->ptr;
1709 buf = expand(val, 0);
1710 *fp = strtod(buf, &endp);
1714 /* invoke a command specific function */
1715 if(c->func) c->func();
1722 Behaviour *b = &uzbl.behave;
1724 if(b->html_buffer->str) {
1725 webkit_web_view_load_html_string (uzbl.gui.web_view,
1726 b->html_buffer->str, b->base_url);
1727 g_string_free(b->html_buffer, TRUE);
1728 b->html_buffer = g_string_new("");
1732 enum {M_CMD, M_HTML};
1734 parse_cmd_line(const char *ctl_line, GString *result) {
1735 Behaviour *b = &uzbl.behave;
1738 if(b->mode == M_HTML) {
1739 len = strlen(b->html_endmarker);
1740 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1741 if(len == strlen(ctl_line)-1 &&
1742 !strncmp(b->html_endmarker, ctl_line, len)) {
1744 set_var_value("mode", "0");
1749 set_timeout(b->html_timeout);
1750 g_string_append(b->html_buffer, ctl_line);
1753 else if((ctl_line[0] == '#') /* Comments */
1754 || (ctl_line[0] == ' ')
1755 || (ctl_line[0] == '\n'))
1756 ; /* ignore these lines */
1757 else { /* parse a command */
1759 gchar **tokens = NULL;
1760 len = strlen(ctl_line);
1762 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1763 ctlstrip = g_strndup(ctl_line, len - 1);
1764 else ctlstrip = g_strdup(ctl_line);
1766 tokens = g_strsplit(ctlstrip, " ", 2);
1767 parse_command(tokens[0], tokens[1], result);
1774 build_stream_name(int type, const gchar* dir) {
1775 State *s = &uzbl.state;
1779 str = g_strdup_printf
1780 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1781 } else if (type == SOCKET) {
1782 str = g_strdup_printf
1783 ("%s/uzbl_socket_%s", dir, s->instance_name);
1789 control_fifo(GIOChannel *gio, GIOCondition condition) {
1790 if (uzbl.state.verbose)
1791 printf("triggered\n");
1796 if (condition & G_IO_HUP)
1797 g_error ("Fifo: Read end of pipe died!\n");
1800 g_error ("Fifo: GIOChannel broke\n");
1802 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1803 if (ret == G_IO_STATUS_ERROR) {
1804 g_error ("Fifo: Error reading: %s\n", err->message);
1808 parse_cmd_line(ctl_line, NULL);
1815 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1816 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1817 if (unlink(uzbl.comm.fifo_path) == -1)
1818 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1819 g_free(uzbl.comm.fifo_path);
1820 uzbl.comm.fifo_path = NULL;
1823 if (*dir == ' ') { /* space unsets the variable */
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);
2214 GtkWidget* create_window () {
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);
2226 GtkPlug* create_plug () {
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 span <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 gchar *actdup = g_strdup(actname);
2252 g_array_append_val(rets, actdup);
2254 if ((g_strcmp0(actname, "spawn") == 0) ||
2255 (g_strcmp0(actname, "sh") == 0) ||
2256 (g_strcmp0(actname, "sync_spawn") == 0) ||
2257 (g_strcmp0(actname, "sync_sh") == 0)) {
2259 GString *a = g_string_new("");
2260 gchar **spawnparts = split_quoted(origargs, FALSE);
2261 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2262 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2264 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2265 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2267 g_array_append_val(rets, a->str);
2268 g_string_free(a, FALSE);
2269 g_strfreev(spawnparts);
2271 gchar *origdup = g_strdup(origargs);
2272 g_array_append_val(rets, origdup);
2274 return (gchar**)g_array_free(rets, FALSE);
2278 run_handler (const gchar *act, const gchar *args) {
2279 /* Consider this code a temporary hack to make the handlers usable.
2280 In practice, all this splicing, injection, and reconstruction is
2281 inefficient, annoying and hard to manage. Potential pitfalls arise
2282 when the handler specific args 1) are not quoted (the handler
2283 callbacks should take care of this) 2) are quoted but interfere
2284 with the users' own quotation. A more ideal solution is
2285 to refactor parse_command so that it doesn't just take a string
2286 and execute it; rather than that, we should have a function which
2287 returns the argument vector parsed from the string. This vector
2288 could be modified (e.g. insert additional args into it) before
2289 passing it to the next function that actually executes it. Though
2290 it still isn't perfect for chain actions.. will reconsider & re-
2291 factor when I have the time. -duc */
2293 char **parts = g_strsplit(act, " ", 2);
2295 if (g_strcmp0(parts[0], "chain") == 0) {
2296 GString *newargs = g_string_new("");
2297 gchar **chainparts = split_quoted(parts[1], FALSE);
2299 /* for every argument in the chain, inject the handler args
2300 and make sure the new parts are wrapped in quotes */
2301 gchar **cp = chainparts;
2303 gchar *quotless = NULL;
2304 gchar **spliced_quotless = NULL; // sigh -_-;
2305 gchar **inpart = NULL;
2308 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2310 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2311 } else quotless = g_strdup(*cp);
2313 spliced_quotless = g_strsplit(quotless, " ", 2);
2314 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2315 g_strfreev(spliced_quotless);
2317 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2323 parse_command(parts[0], &(newargs->str[1]), NULL);
2324 g_string_free(newargs, TRUE);
2325 g_strfreev(chainparts);
2328 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2329 parse_command(inparts[0], inparts[1], NULL);
2337 add_binding (const gchar *key, const gchar *act) {
2338 char **parts = g_strsplit(act, " ", 2);
2345 if (uzbl.state.verbose)
2346 printf ("Binding %-10s : %s\n", key, act);
2347 action = new_action(parts[0], parts[1]);
2349 if (g_hash_table_remove (uzbl.bindings, key))
2350 g_warning ("Overwriting existing binding for \"%s\"", key);
2351 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2356 get_xdg_var (XDG_Var xdg) {
2357 const gchar* actual_value = getenv (xdg.environmental);
2358 const gchar* home = getenv ("HOME");
2359 gchar* return_value;
2361 if (! actual_value || strcmp (actual_value, "") == 0) {
2362 if (xdg.default_value) {
2363 return_value = str_replace ("~", home, xdg.default_value);
2365 return_value = NULL;
2368 return_value = str_replace("~", home, actual_value);
2371 return return_value;
2375 find_xdg_file (int xdg_type, char* filename) {
2376 /* xdg_type = 0 => config
2377 xdg_type = 1 => data
2378 xdg_type = 2 => cache*/
2380 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2381 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2384 gchar* temporary_string;
2388 if (! file_exists (temporary_file) && xdg_type != 2) {
2389 buf = get_xdg_var (XDG[3 + xdg_type]);
2390 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2393 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2394 g_free (temporary_file);
2395 temporary_file = g_strconcat (temporary_string, filename, NULL);
2399 //g_free (temporary_string); - segfaults.
2401 if (file_exists (temporary_file)) {
2402 return temporary_file;
2409 State *s = &uzbl.state;
2410 Network *n = &uzbl.net;
2412 for (i = 0; default_config[i].command != NULL; i++) {
2413 parse_cmd_line(default_config[i].command, NULL);
2416 if (g_strcmp0(s->config_file, "-") == 0) {
2417 s->config_file = NULL;
2421 else if (!s->config_file) {
2422 s->config_file = find_xdg_file (0, "/uzbl/config");
2425 if (s->config_file) {
2426 GArray* lines = read_file_by_line (s->config_file);
2430 while ((line = g_array_index(lines, gchar*, i))) {
2431 parse_cmd_line (line, NULL);
2435 g_array_free (lines, TRUE);
2437 if (uzbl.state.verbose)
2438 printf ("No configuration file loaded.\n");
2441 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2444 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2447 if (!uzbl.behave.cookie_handler)
2450 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2451 GString *s = g_string_new ("");
2452 SoupURI * soup_uri = soup_message_get_uri(msg);
2453 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2454 run_handler(uzbl.behave.cookie_handler, s->str);
2456 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2457 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2458 if ( p != NULL ) *p = '\0';
2459 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2461 if (uzbl.comm.sync_stdout)
2462 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2464 g_string_free(s, TRUE);
2468 save_cookies (SoupMessage *msg, gpointer user_data){
2472 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2473 cookie = soup_cookie_to_set_cookie_header(ck->data);
2474 SoupURI * soup_uri = soup_message_get_uri(msg);
2475 GString *s = g_string_new ("");
2476 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2477 run_handler(uzbl.behave.cookie_handler, s->str);
2479 g_string_free(s, TRUE);
2484 /* --- WEBINSPECTOR --- */
2486 hide_window_cb(GtkWidget *widget, gpointer data) {
2489 gtk_widget_hide(widget);
2492 static WebKitWebView*
2493 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2496 (void) web_inspector;
2497 GtkWidget* scrolled_window;
2498 GtkWidget* new_web_view;
2501 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2502 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2503 G_CALLBACK(hide_window_cb), NULL);
2505 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2506 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2507 gtk_widget_show(g->inspector_window);
2509 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2510 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2511 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2512 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2513 gtk_widget_show(scrolled_window);
2515 new_web_view = webkit_web_view_new();
2516 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2518 return WEBKIT_WEB_VIEW(new_web_view);
2522 inspector_show_window_cb (WebKitWebInspector* inspector){
2524 gtk_widget_show(uzbl.gui.inspector_window);
2528 /* TODO: Add variables and code to make use of these functions */
2530 inspector_close_window_cb (WebKitWebInspector* inspector){
2536 inspector_attach_window_cb (WebKitWebInspector* inspector){
2542 inspector_detach_window_cb (WebKitWebInspector* inspector){
2548 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2554 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2560 set_up_inspector() {
2562 WebKitWebSettings *settings = view_settings();
2563 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2565 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2566 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2567 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2568 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2569 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2570 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2571 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2573 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2577 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2579 uzbl_cmdprop *c = v;
2584 if(c->type == TYPE_STR)
2585 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2586 else if(c->type == TYPE_INT)
2587 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2591 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2595 printf("bind %s = %s %s\n", (char *)k ,
2596 (char *)a->name, a->param?(char *)a->param:"");
2600 dump_config() { //ADD "result" var so we can use this with uzblctrl
2601 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2602 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2606 retreive_geometry() {
2608 GString *buf = g_string_new("");
2610 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2611 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2613 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2615 if(uzbl.gui.geometry)
2616 g_free(uzbl.gui.geometry);
2617 uzbl.gui.geometry = g_string_free(buf, FALSE);
2622 main (int argc, char* argv[]) {
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("=");
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");
2671 set_insert_mode(FALSE);
2673 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2674 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2675 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2676 uzbl.info.arch = ARCH;
2677 uzbl.info.commit = COMMIT;
2680 make_var_to_name_hash();
2682 uzbl.gui.scrolled_win = create_browser();
2685 #ifndef UZBL_LIBRARY
2688 main (int argc, char* argv[]) {
2689 initialize(argc, argv);
2691 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2695 /* initial packing */
2696 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2697 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2699 if (uzbl.state.socket_id) {
2700 uzbl.gui.plug = create_plug ();
2701 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2702 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2704 uzbl.gui.main_window = create_window ();
2705 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2706 gtk_widget_show_all (uzbl.gui.main_window);
2707 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2710 if(!uzbl.state.instance_name)
2711 uzbl.state.instance_name = itos((int)uzbl.xwin);
2713 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2715 if (uzbl.state.verbose) {
2716 printf("Uzbl start location: %s\n", argv[0]);
2717 if (uzbl.state.socket_id)
2718 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2720 printf("window_id %i\n",(int) uzbl.xwin);
2721 printf("pid %i\n", getpid ());
2722 printf("name: %s\n", uzbl.state.instance_name);
2725 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2726 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2727 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2728 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2729 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2731 if(uzbl.gui.geometry)
2734 retreive_geometry();
2736 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2737 gboolean verbose_override = uzbl.state.verbose;
2741 if (!uzbl.behave.show_status)
2742 gtk_widget_hide(uzbl.gui.mainbar);
2749 if (verbose_override > uzbl.state.verbose)
2750 uzbl.state.verbose = verbose_override;
2753 set_var_value("uri", uri_override);
2754 g_free(uri_override);
2755 } else if (uzbl.state.uri)
2756 cmd_load_uri(uzbl.gui.web_view, NULL);
2761 return EXIT_SUCCESS;
2765 /* vi: set et ts=4: */