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 { "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 {
92 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
94 /* abbreviations to help keep the table's width humane */
95 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
96 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
101 } var_name_to_ptr[] = {
102 /* variable name pointer to variable in code type dump callback function */
103 /* ---------------------------------------------------------------------------------------------- */
104 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
105 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
106 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
107 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
108 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
109 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
110 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
111 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, NULL)}, /* XXX */
112 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
113 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
114 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
115 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
116 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
117 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
118 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
119 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
120 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_title)},
121 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
122 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
123 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
124 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
125 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, NULL)}, /* XXX */
126 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
127 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
128 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
129 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
130 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
131 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
132 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
133 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
134 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
135 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
136 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
137 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
138 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
139 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
140 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
141 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
142 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
143 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
144 /* exported WebKitWebSettings properties */
145 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
146 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
147 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
148 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
149 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
150 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
151 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
152 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
153 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
154 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
155 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
156 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
157 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
158 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
159 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
160 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
162 /* constants (not dumpable or writeable) */
163 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
164 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
165 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
166 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
167 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
168 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
169 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
170 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
171 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
172 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
173 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
175 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
176 }, *n2v_p = var_name_to_ptr;
183 { "SHIFT", GDK_SHIFT_MASK }, // shift
184 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
185 { "CONTROL", GDK_CONTROL_MASK }, // control
186 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
187 { "MOD2", GDK_MOD2_MASK }, // 5th mod
188 { "MOD3", GDK_MOD3_MASK }, // 6th mod
189 { "MOD4", GDK_MOD4_MASK }, // 7th mod
190 { "MOD5", GDK_MOD5_MASK }, // 8th mod
191 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
192 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
193 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
194 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
195 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
196 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
197 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
198 { "META", GDK_META_MASK }, // meta (since 2.10)
203 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
204 * for quick access */
206 make_var_to_name_hash() {
207 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
209 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
214 /* --- UTILITY FUNCTIONS --- */
216 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
218 get_exp_type(gchar *s) {
222 else if(*(s+1) == '{')
223 return EXP_BRACED_VAR;
224 else if(*(s+1) == '<')
226 else if(*(s+1) == '[')
229 return EXP_SIMPLE_VAR;
235 * recurse == 1: don't expand '@(command)@'
236 * recurse == 2: don't expand '@<java script>@'
239 expand(char *s, guint recurse) {
243 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
248 gchar *cmd_stdout = NULL;
250 GString *buf = g_string_new("");
251 GString *js_ret = g_string_new("");
256 g_string_append_c(buf, *++s);
261 etype = get_exp_type(s);
266 if( (vend = strpbrk(s, end_simple_var)) ||
267 (vend = strchr(s, '\0')) ) {
268 strncpy(ret, s, vend-s);
274 if( (vend = strchr(s, upto)) ||
275 (vend = strchr(s, '\0')) ) {
276 strncpy(ret, s, vend-s);
282 strcpy(str_end, ")@");
284 if( (vend = strstr(s, str_end)) ||
285 (vend = strchr(s, '\0')) ) {
286 strncpy(ret, s, vend-s);
292 strcpy(str_end, ">@");
294 if( (vend = strstr(s, str_end)) ||
295 (vend = strchr(s, '\0')) ) {
296 strncpy(ret, s, vend-s);
302 strcpy(str_end, "]@");
304 if( (vend = strstr(s, str_end)) ||
305 (vend = strchr(s, '\0')) ) {
306 strncpy(ret, s, vend-s);
312 if(etype == EXP_SIMPLE_VAR ||
313 etype == EXP_BRACED_VAR) {
314 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
315 if(c->type == TYPE_STR && *c->ptr != NULL) {
316 g_string_append(buf, (gchar *)*c->ptr);
317 } else if(c->type == TYPE_INT) {
318 char *b = itos((uintptr_t)*c->ptr);
319 g_string_append(buf, b);
324 if(etype == EXP_SIMPLE_VAR)
329 else if(recurse != 1 &&
331 mycmd = expand(ret, 1);
332 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
336 g_printerr("error on running command: %s\n", err->message);
339 else if (*cmd_stdout) {
340 int len = strlen(cmd_stdout);
342 if(cmd_stdout[len-1] == '\n')
343 cmd_stdout[--len] = 0; /* strip trailing newline */
345 g_string_append(buf, cmd_stdout);
350 else if(recurse != 2 &&
352 mycmd = expand(ret, 2);
353 eval_js(uzbl.gui.web_view, mycmd, js_ret);
357 g_string_append(buf, js_ret->str);
358 g_string_free(js_ret, TRUE);
359 js_ret = g_string_new("");
363 else if(etype == EXP_ESCAPE) {
364 mycmd = expand(ret, 0);
365 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
367 g_string_append(buf, escaped);
376 g_string_append_c(buf, *s);
381 g_string_free(js_ret, TRUE);
382 return g_string_free(buf, FALSE);
389 snprintf(tmp, sizeof(tmp), "%i", val);
390 return g_strdup(tmp);
394 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
397 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
400 str_replace (const char* search, const char* replace, const char* string) {
404 buf = g_strsplit (string, search, -1);
405 ret = g_strjoinv (replace, buf);
406 g_strfreev(buf); // somebody said this segfaults
412 read_file_by_line (gchar *path) {
413 GIOChannel *chan = NULL;
414 gchar *readbuf = NULL;
416 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
419 chan = g_io_channel_new_file(path, "r", NULL);
422 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
423 const gchar* val = g_strdup (readbuf);
424 g_array_append_val (lines, val);
429 g_io_channel_unref (chan);
431 fprintf(stderr, "File '%s' not be read.\n", path);
438 gchar* parseenv (char* string) {
439 extern char** environ;
440 gchar* tmpstr = NULL;
444 while (environ[i] != NULL) {
445 gchar** env = g_strsplit (environ[i], "=", 2);
446 gchar* envname = g_strconcat ("$", env[0], NULL);
448 if (g_strrstr (string, envname) != NULL) {
449 tmpstr = g_strdup(string);
451 string = str_replace(envname, env[1], tmpstr);
456 g_strfreev (env); // somebody said this breaks uzbl
464 setup_signal(int signr, sigfunc *shandler) {
465 struct sigaction nh, oh;
467 nh.sa_handler = shandler;
468 sigemptyset(&nh.sa_mask);
471 if(sigaction(signr, &nh, &oh) < 0)
479 if (uzbl.behave.fifo_dir)
480 unlink (uzbl.comm.fifo_path);
481 if (uzbl.behave.socket_dir)
482 unlink (uzbl.comm.socket_path);
484 g_free(uzbl.state.executable_path);
485 g_free(uzbl.state.keycmd);
486 g_hash_table_destroy(uzbl.bindings);
487 g_hash_table_destroy(uzbl.behave.commands);
490 /* used for html_mode_timeout
491 * be sure to extend this function to use
492 * more timers if needed in other places
495 set_timeout(int seconds) {
497 memset(&t, 0, sizeof t);
499 t.it_value.tv_sec = seconds;
500 t.it_value.tv_usec = 0;
501 setitimer(ITIMER_REAL, &t, NULL);
504 /* --- SIGNAL HANDLER --- */
507 catch_sigterm(int s) {
513 catch_sigint(int s) {
523 set_var_value("mode", "0");
528 /* --- CALLBACKS --- */
531 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
534 (void) navigation_action;
535 (void) policy_decision;
537 const gchar* uri = webkit_network_request_get_uri (request);
538 if (uzbl.state.verbose)
539 printf("New window requested -> %s \n", uri);
540 webkit_web_policy_decision_use(policy_decision);
545 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
550 /* If we can display it, let's display it... */
551 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
552 webkit_web_policy_decision_use (policy_decision);
556 /* ...everything we can't displayed is downloaded */
557 webkit_web_policy_decision_download (policy_decision);
562 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
566 if (uzbl.state.selected_url != NULL) {
567 if (uzbl.state.verbose)
568 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
569 new_window_load_uri(uzbl.state.selected_url);
571 if (uzbl.state.verbose)
572 printf("New web view -> %s\n","Nothing to open, exiting");
578 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
581 if (uzbl.behave.download_handler) {
582 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
583 if (uzbl.state.verbose)
584 printf("Download -> %s\n",uri);
585 /* if urls not escaped, we may have to escape and quote uri before this call */
586 run_handler(uzbl.behave.download_handler, uri);
591 /* scroll a bar in a given direction */
593 scroll (GtkAdjustment* bar, GArray *argv) {
597 gdouble page_size = gtk_adjustment_get_page_size(bar);
598 gdouble value = gtk_adjustment_get_value(bar);
599 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
602 value += page_size * amount * 0.01;
606 max_value = gtk_adjustment_get_upper(bar) - page_size;
608 if (value > max_value)
609 value = max_value; /* don't scroll past the end of the page */
611 gtk_adjustment_set_value (bar, value);
615 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
616 (void) page; (void) argv; (void) result;
617 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
621 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
622 (void) page; (void) argv; (void) result;
623 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
624 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
628 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
629 (void) page; (void) result;
630 scroll(uzbl.gui.bar_v, argv);
634 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
635 (void) page; (void) result;
636 scroll(uzbl.gui.bar_h, argv);
641 if (!uzbl.behave.show_status) {
642 gtk_widget_hide(uzbl.gui.mainbar);
644 gtk_widget_show(uzbl.gui.mainbar);
650 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
655 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
659 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
664 if (uzbl.behave.show_status) {
665 gtk_widget_hide(uzbl.gui.mainbar);
667 gtk_widget_show(uzbl.gui.mainbar);
669 uzbl.behave.show_status = !uzbl.behave.show_status;
674 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
678 //Set selected_url state variable
679 g_free(uzbl.state.selected_url);
680 uzbl.state.selected_url = NULL;
682 uzbl.state.selected_url = g_strdup(link);
688 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
691 const gchar *title = webkit_web_view_get_title(web_view);
692 if (uzbl.gui.main_title)
693 g_free (uzbl.gui.main_title);
694 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
699 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
702 uzbl.gui.sbar.load_progress = progress;
704 g_free(uzbl.gui.sbar.progress_bar);
705 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
711 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
715 if (uzbl.behave.load_finish_handler)
716 run_handler(uzbl.behave.load_finish_handler, "");
719 void clear_keycmd() {
720 g_free(uzbl.state.keycmd);
721 uzbl.state.keycmd = g_strdup("");
725 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
729 uzbl.gui.sbar.load_progress = 0;
730 clear_keycmd(); // don't need old commands to remain on new page?
731 if (uzbl.behave.load_start_handler)
732 run_handler(uzbl.behave.load_start_handler, "");
736 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
739 g_free (uzbl.state.uri);
740 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
741 uzbl.state.uri = g_string_free (newuri, FALSE);
742 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
743 set_insert_mode(uzbl.behave.always_insert_mode);
746 if (uzbl.behave.load_commit_handler)
747 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
751 destroy_cb (GtkWidget* widget, gpointer data) {
759 if (uzbl.behave.history_handler) {
761 struct tm * timeinfo;
764 timeinfo = localtime ( &rawtime );
765 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
766 run_handler(uzbl.behave.history_handler, date);
771 /* VIEW funcs (little webkit wrappers) */
772 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
774 VIEWFUNC(reload_bypass_cache)
775 VIEWFUNC(stop_loading)
782 /* -- command to callback/function map for things we cannot attach to any signals */
783 static struct {char *key; CommandInfo value;} cmdlist[] =
784 { /* key function no_split */
785 { "back", {view_go_back, 0} },
786 { "forward", {view_go_forward, 0} },
787 { "scroll_vert", {scroll_vert, 0} },
788 { "scroll_horz", {scroll_horz, 0} },
789 { "scroll_begin", {scroll_begin, 0} },
790 { "scroll_end", {scroll_end, 0} },
791 { "reload", {view_reload, 0}, },
792 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
793 { "stop", {view_stop_loading, 0}, },
794 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
795 { "zoom_out", {view_zoom_out, 0}, },
796 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
797 { "uri", {load_uri, TRUE} },
798 { "js", {run_js, TRUE} },
799 { "script", {run_external_js, 0} },
800 { "toggle_status", {toggle_status_cb, 0} },
801 { "spawn", {spawn, 0} },
802 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
803 { "sh", {spawn_sh, 0} },
804 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
805 { "exit", {close_uzbl, 0} },
806 { "search", {search_forward_text, TRUE} },
807 { "search_reverse", {search_reverse_text, TRUE} },
808 { "dehilight", {dehilight, 0} },
809 { "toggle_insert_mode", {toggle_insert_mode, 0} },
810 { "set", {set_var, TRUE} },
811 //{ "get", {get_var, TRUE} },
812 { "bind", {act_bind, TRUE} },
813 { "dump_config", {act_dump_config, 0} },
814 { "keycmd", {keycmd, TRUE} },
815 { "keycmd_nl", {keycmd_nl, TRUE} },
816 { "keycmd_bs", {keycmd_bs, 0} },
817 { "chain", {chain, 0} },
818 { "print", {print, TRUE} }
825 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
827 for (i = 0; i < LENGTH(cmdlist); i++)
828 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
831 /* -- CORE FUNCTIONS -- */
834 free_action(gpointer act) {
835 Action *action = (Action*)act;
836 g_free(action->name);
838 g_free(action->param);
843 new_action(const gchar *name, const gchar *param) {
844 Action *action = g_new(Action, 1);
846 action->name = g_strdup(name);
848 action->param = g_strdup(param);
850 action->param = NULL;
856 file_exists (const char * filename) {
857 return (access(filename, F_OK) == 0);
861 set_var(WebKitWebView *page, GArray *argv, GString *result) {
862 (void) page; (void) result;
863 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
864 if (split[0] != NULL) {
865 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
866 set_var_value(g_strstrip(split[0]), value);
873 print(WebKitWebView *page, GArray *argv, GString *result) {
874 (void) page; (void) result;
877 buf = expand(argv_idx(argv, 0), 0);
878 g_string_assign(result, buf);
883 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
884 (void) page; (void) result;
885 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
886 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
887 add_binding(g_strstrip(split[0]), value);
898 /* XXX set_var_value instead? */
899 void set_insert_mode(gboolean mode) {
900 uzbl.behave.insert_mode = mode;
901 uzbl.gui.sbar.mode_indicator = (mode ?
902 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
906 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
907 (void) page; (void) result;
909 if (argv_idx(argv, 0)) {
910 if (strcmp (argv_idx(argv, 0), "0") == 0) {
911 set_insert_mode(FALSE);
913 set_insert_mode(TRUE);
916 set_insert_mode( !uzbl.behave.insert_mode );
923 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
926 if (argv_idx(argv, 0)) {
927 GString* newuri = g_string_new (argv_idx(argv, 0));
928 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
929 run_js(web_view, argv, NULL);
932 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
933 g_string_prepend (newuri, "http://");
934 /* if we do handle cookies, ask our handler for them */
935 webkit_web_view_load_uri (web_view, newuri->str);
936 g_string_free (newuri, TRUE);
944 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
945 size_t argumentCount, const JSValueRef arguments[],
946 JSValueRef* exception) {
951 JSStringRef js_result_string;
952 GString *result = g_string_new("");
954 if (argumentCount >= 1) {
955 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
956 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
957 char ctl_line[arg_size];
958 JSStringGetUTF8CString(arg, ctl_line, arg_size);
960 parse_cmd_line(ctl_line, result);
962 JSStringRelease(arg);
964 js_result_string = JSStringCreateWithUTF8CString(result->str);
966 g_string_free(result, TRUE);
968 return JSValueMakeString(ctx, js_result_string);
971 static JSStaticFunction js_static_functions[] = {
972 {"run", js_run_command, kJSPropertyAttributeNone},
977 /* This function creates the class and its definition, only once */
978 if (!uzbl.js.initialized) {
979 /* it would be pretty cool to make this dynamic */
980 uzbl.js.classdef = kJSClassDefinitionEmpty;
981 uzbl.js.classdef.staticFunctions = js_static_functions;
983 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
989 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
990 WebKitWebFrame *frame;
991 JSGlobalContextRef context;
992 JSObjectRef globalobject;
993 JSStringRef var_name;
995 JSStringRef js_script;
996 JSValueRef js_result;
997 JSStringRef js_result_string;
998 size_t js_result_size;
1002 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1003 context = webkit_web_frame_get_global_context(frame);
1004 globalobject = JSContextGetGlobalObject(context);
1006 /* uzbl javascript namespace */
1007 var_name = JSStringCreateWithUTF8CString("Uzbl");
1008 JSObjectSetProperty(context, globalobject, var_name,
1009 JSObjectMake(context, uzbl.js.classref, NULL),
1010 kJSClassAttributeNone, NULL);
1012 /* evaluate the script and get return value*/
1013 js_script = JSStringCreateWithUTF8CString(script);
1014 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1015 if (js_result && !JSValueIsUndefined(context, js_result)) {
1016 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1017 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1019 if (js_result_size) {
1020 char js_result_utf8[js_result_size];
1021 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1022 g_string_assign(result, js_result_utf8);
1025 JSStringRelease(js_result_string);
1029 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1031 JSStringRelease(var_name);
1032 JSStringRelease(js_script);
1036 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1038 if (argv_idx(argv, 0))
1039 eval_js(web_view, argv_idx(argv, 0), result);
1043 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1045 if (argv_idx(argv, 0)) {
1046 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1051 while ((line = g_array_index(lines, gchar*, i))) {
1053 js = g_strdup (line);
1055 gchar* newjs = g_strconcat (js, line, NULL);
1062 if (uzbl.state.verbose)
1063 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1065 if (argv_idx (argv, 1)) {
1066 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1070 eval_js (web_view, js, result);
1072 g_array_free (lines, TRUE);
1077 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1078 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1079 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1080 webkit_web_view_unmark_text_matches (page);
1081 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1082 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1086 if (uzbl.state.searchtx) {
1087 if (uzbl.state.verbose)
1088 printf ("Searching: %s\n", uzbl.state.searchtx);
1089 webkit_web_view_set_highlight_text_matches (page, TRUE);
1090 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1095 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1097 search_text(page, argv, TRUE);
1101 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1103 search_text(page, argv, FALSE);
1107 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1108 (void) argv; (void) result;
1109 webkit_web_view_set_highlight_text_matches (page, FALSE);
1114 new_window_load_uri (const gchar * uri) {
1115 if (uzbl.behave.new_window) {
1116 GString *s = g_string_new ("");
1117 g_string_printf(s, "'%s'", uri);
1118 run_handler(uzbl.behave.new_window, s->str);
1121 GString* to_execute = g_string_new ("");
1122 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1124 for (i = 0; entries[i].long_name != NULL; i++) {
1125 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1126 gchar** str = (gchar**)entries[i].arg_data;
1128 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1132 if (uzbl.state.verbose)
1133 printf("\n%s\n", to_execute->str);
1134 g_spawn_command_line_async (to_execute->str, NULL);
1135 g_string_free (to_execute, TRUE);
1139 chain (WebKitWebView *page, GArray *argv, GString *result) {
1140 (void) page; (void) result;
1142 gchar **parts = NULL;
1144 while ((a = argv_idx(argv, i++))) {
1145 parts = g_strsplit (a, " ", 2);
1146 parse_command(parts[0], parts[1], result);
1152 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1156 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1162 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1166 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1172 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1177 int len = strlen(uzbl.state.keycmd);
1178 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1180 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1185 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1192 /* --Statusbar functions-- */
1194 build_progressbar_ascii(int percent) {
1195 int width=uzbl.gui.sbar.progress_w;
1198 GString *bar = g_string_new("");
1200 l = (double)percent*((double)width/100.);
1201 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1203 for(i=0; i<(int)l; i++)
1204 g_string_append(bar, uzbl.gui.sbar.progress_s);
1207 g_string_append(bar, uzbl.gui.sbar.progress_u);
1209 return g_string_free(bar, FALSE);
1211 /* --End Statusbar functions-- */
1214 sharg_append(GArray *a, const gchar *str) {
1215 const gchar *s = (str ? str : "");
1216 g_array_append_val(a, s);
1219 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1221 run_command (const gchar *command, const guint npre, const gchar **args,
1222 const gboolean sync, char **output_stdout) {
1223 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1226 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1227 gchar *pid = itos(getpid());
1228 gchar *xwin = itos(uzbl.xwin);
1230 sharg_append(a, command);
1231 for (i = 0; i < npre; i++) /* add n args before the default vars */
1232 sharg_append(a, args[i]);
1233 sharg_append(a, uzbl.state.config_file);
1234 sharg_append(a, pid);
1235 sharg_append(a, xwin);
1236 sharg_append(a, uzbl.comm.fifo_path);
1237 sharg_append(a, uzbl.comm.socket_path);
1238 sharg_append(a, uzbl.state.uri);
1239 sharg_append(a, uzbl.gui.main_title);
1241 for (i = npre; i < g_strv_length((gchar**)args); i++)
1242 sharg_append(a, args[i]);
1246 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1248 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1249 NULL, NULL, output_stdout, NULL, NULL, &err);
1250 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1251 NULL, NULL, NULL, &err);
1253 if (uzbl.state.verbose) {
1254 GString *s = g_string_new("spawned:");
1255 for (i = 0; i < (a->len); i++) {
1256 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1257 g_string_append_printf(s, " %s", qarg);
1260 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1261 printf("%s\n", s->str);
1262 g_string_free(s, TRUE);
1264 printf("Stdout: %s\n", *output_stdout);
1268 g_printerr("error on run_command: %s\n", err->message);
1273 g_array_free (a, TRUE);
1278 split_quoted(const gchar* src, const gboolean unquote) {
1279 /* split on unquoted space, return array of strings;
1280 remove a layer of quotes and backslashes if unquote */
1281 if (!src) return NULL;
1283 gboolean dq = FALSE;
1284 gboolean sq = FALSE;
1285 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1286 GString *s = g_string_new ("");
1290 for (p = src; *p != '\0'; p++) {
1291 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1292 else if (*p == '\\') { g_string_append_c(s, *p++);
1293 g_string_append_c(s, *p); }
1294 else if ((*p == '"') && unquote && !sq) dq = !dq;
1295 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1297 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1298 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1300 else if ((*p == ' ') && !dq && !sq) {
1301 dup = g_strdup(s->str);
1302 g_array_append_val(a, dup);
1303 g_string_truncate(s, 0);
1304 } else g_string_append_c(s, *p);
1306 dup = g_strdup(s->str);
1307 g_array_append_val(a, dup);
1308 ret = (gchar**)a->data;
1309 g_array_free (a, FALSE);
1310 g_string_free (s, TRUE);
1315 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1316 (void)web_view; (void)result;
1317 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1318 if (argv_idx(argv, 0))
1319 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1323 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1324 (void)web_view; (void)result;
1326 if (argv_idx(argv, 0))
1327 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1328 TRUE, &uzbl.comm.sync_stdout);
1332 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1333 (void)web_view; (void)result;
1334 if (!uzbl.behave.shell_cmd) {
1335 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1340 gchar *spacer = g_strdup("");
1341 g_array_insert_val(argv, 1, spacer);
1342 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1344 for (i = 1; i < g_strv_length(cmd); i++)
1345 g_array_prepend_val(argv, cmd[i]);
1347 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1353 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1354 (void)web_view; (void)result;
1355 if (!uzbl.behave.shell_cmd) {
1356 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1361 gchar *spacer = g_strdup("");
1362 g_array_insert_val(argv, 1, spacer);
1363 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1365 for (i = 1; i < g_strv_length(cmd); i++)
1366 g_array_prepend_val(argv, cmd[i]);
1368 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1369 TRUE, &uzbl.comm.sync_stdout);
1375 parse_command(const char *cmd, const char *param, GString *result) {
1378 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1380 gchar **par = split_quoted(param, TRUE);
1381 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1383 if (c->no_split) { /* don't split */
1384 sharg_append(a, param);
1386 for (i = 0; i < g_strv_length(par); i++)
1387 sharg_append(a, par[i]);
1390 if (result == NULL) {
1391 GString *result_print = g_string_new("");
1393 c->function(uzbl.gui.web_view, a, result_print);
1394 if (result_print->len)
1395 printf("%*s\n", result_print->len, result_print->str);
1397 g_string_free(result_print, TRUE);
1399 c->function(uzbl.gui.web_view, a, result);
1402 g_array_free (a, TRUE);
1405 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1412 if(*uzbl.net.proxy_url == ' '
1413 || uzbl.net.proxy_url == NULL) {
1414 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1415 (GType) SOUP_SESSION_PROXY_URI);
1418 suri = soup_uri_new(uzbl.net.proxy_url);
1419 g_object_set(G_OBJECT(uzbl.net.soup_session),
1420 SOUP_SESSION_PROXY_URI,
1422 soup_uri_free(suri);
1429 if(file_exists(uzbl.gui.icon)) {
1430 if (uzbl.gui.main_window)
1431 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1433 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1439 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1440 g_array_append_val (a, uzbl.state.uri);
1441 load_uri(uzbl.gui.web_view, a, NULL);
1442 g_array_free (a, TRUE);
1446 cmd_always_insert_mode() {
1447 set_insert_mode(uzbl.behave.always_insert_mode);
1453 g_object_set(G_OBJECT(uzbl.net.soup_session),
1454 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1458 cmd_max_conns_host() {
1459 g_object_set(G_OBJECT(uzbl.net.soup_session),
1460 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1465 soup_session_remove_feature
1466 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1467 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1468 /*g_free(uzbl.net.soup_logger);*/
1470 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1471 soup_session_add_feature(uzbl.net.soup_session,
1472 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1475 static WebKitWebSettings*
1477 return webkit_web_view_get_settings(uzbl.gui.web_view);
1482 WebKitWebSettings *ws = view_settings();
1483 if (uzbl.behave.font_size > 0) {
1484 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1487 if (uzbl.behave.monospace_size > 0) {
1488 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1489 uzbl.behave.monospace_size, NULL);
1491 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1492 uzbl.behave.font_size, NULL);
1498 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1502 cmd_disable_plugins() {
1503 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1504 !uzbl.behave.disable_plugins, NULL);
1508 cmd_disable_scripts() {
1509 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1510 !uzbl.behave.disable_scripts, NULL);
1514 cmd_minimum_font_size() {
1515 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1516 uzbl.behave.minimum_font_size, NULL);
1519 cmd_autoload_img() {
1520 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1521 uzbl.behave.autoload_img, NULL);
1526 cmd_autoshrink_img() {
1527 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1528 uzbl.behave.autoshrink_img, NULL);
1533 cmd_enable_spellcheck() {
1534 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1535 uzbl.behave.enable_spellcheck, NULL);
1539 cmd_enable_private() {
1540 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1541 uzbl.behave.enable_private, NULL);
1546 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1547 uzbl.behave.print_bg, NULL);
1552 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1553 uzbl.behave.style_uri, NULL);
1557 cmd_resizable_txt() {
1558 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1559 uzbl.behave.resizable_txt, NULL);
1563 cmd_default_encoding() {
1564 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1565 uzbl.behave.default_encoding, NULL);
1569 cmd_enforce_96dpi() {
1570 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1571 uzbl.behave.enforce_96dpi, NULL);
1575 cmd_caret_browsing() {
1576 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1577 uzbl.behave.caret_browsing, NULL);
1581 cmd_cookie_handler() {
1582 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1583 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1584 if ((g_strcmp0(split[0], "sh") == 0) ||
1585 (g_strcmp0(split[0], "spawn") == 0)) {
1586 g_free (uzbl.behave.cookie_handler);
1587 uzbl.behave.cookie_handler =
1588 g_strdup_printf("sync_%s %s", split[0], split[1]);
1595 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1596 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1597 if ((g_strcmp0(split[0], "sh") == 0) ||
1598 (g_strcmp0(split[0], "spawn") == 0)) {
1599 g_free (uzbl.behave.new_window);
1600 uzbl.behave.new_window =
1601 g_strdup_printf("%s %s", split[0], split[1]);
1608 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1613 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1618 if(uzbl.behave.inject_html) {
1619 webkit_web_view_load_html_string (uzbl.gui.web_view,
1620 uzbl.behave.inject_html, NULL);
1629 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1630 uzbl.behave.modmask = 0;
1632 if(uzbl.behave.modkey)
1633 g_free(uzbl.behave.modkey);
1634 uzbl.behave.modkey = buf;
1636 for (i = 0; modkeys[i].key != NULL; i++) {
1637 if (g_strrstr(buf, modkeys[i].key))
1638 uzbl.behave.modmask |= modkeys[i].mask;
1644 if (*uzbl.net.useragent == ' ') {
1645 g_free (uzbl.net.useragent);
1646 uzbl.net.useragent = NULL;
1648 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1649 uzbl.net.useragent, NULL);
1655 gtk_widget_ref(uzbl.gui.scrolled_win);
1656 gtk_widget_ref(uzbl.gui.mainbar);
1657 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1658 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1660 if(uzbl.behave.status_top) {
1661 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1662 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1665 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1666 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1668 gtk_widget_unref(uzbl.gui.scrolled_win);
1669 gtk_widget_unref(uzbl.gui.mainbar);
1670 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1675 set_var_value(gchar *name, gchar *val) {
1676 uzbl_cmdprop *c = NULL;
1680 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1681 if(!c->writeable) return TRUE;
1683 /* check for the variable type */
1684 if (c->type == TYPE_STR) {
1685 buf = expand(val, 0);
1688 } else if(c->type == TYPE_INT) {
1689 int *ip = (int *)c->ptr;
1690 buf = expand(val, 0);
1691 *ip = (int)strtoul(buf, &endp, 10);
1693 } else if (c->type == TYPE_FLOAT) {
1694 float *fp = (float *)c->ptr;
1695 buf = expand(val, 0);
1696 *fp = strtod(buf, &endp);
1700 /* invoke a command specific function */
1701 if(c->func) c->func();
1708 Behaviour *b = &uzbl.behave;
1710 if(b->html_buffer->str) {
1711 webkit_web_view_load_html_string (uzbl.gui.web_view,
1712 b->html_buffer->str, b->base_url);
1713 g_string_free(b->html_buffer, TRUE);
1714 b->html_buffer = g_string_new("");
1718 enum {M_CMD, M_HTML};
1720 parse_cmd_line(const char *ctl_line, GString *result) {
1721 Behaviour *b = &uzbl.behave;
1724 if(b->mode == M_HTML) {
1725 len = strlen(b->html_endmarker);
1726 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1727 if(len == strlen(ctl_line)-1 &&
1728 !strncmp(b->html_endmarker, ctl_line, len)) {
1730 set_var_value("mode", "0");
1735 set_timeout(b->html_timeout);
1736 g_string_append(b->html_buffer, ctl_line);
1739 else if((ctl_line[0] == '#') /* Comments */
1740 || (ctl_line[0] == ' ')
1741 || (ctl_line[0] == '\n'))
1742 ; /* ignore these lines */
1743 else { /* parse a command */
1745 gchar **tokens = NULL;
1746 len = strlen(ctl_line);
1748 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1749 ctlstrip = g_strndup(ctl_line, len - 1);
1750 else ctlstrip = g_strdup(ctl_line);
1752 tokens = g_strsplit(ctlstrip, " ", 2);
1753 parse_command(tokens[0], tokens[1], result);
1760 build_stream_name(int type, const gchar* dir) {
1761 State *s = &uzbl.state;
1765 str = g_strdup_printf
1766 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1767 } else if (type == SOCKET) {
1768 str = g_strdup_printf
1769 ("%s/uzbl_socket_%s", dir, s->instance_name);
1775 control_fifo(GIOChannel *gio, GIOCondition condition) {
1776 if (uzbl.state.verbose)
1777 printf("triggered\n");
1782 if (condition & G_IO_HUP)
1783 g_error ("Fifo: Read end of pipe died!\n");
1786 g_error ("Fifo: GIOChannel broke\n");
1788 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1789 if (ret == G_IO_STATUS_ERROR) {
1790 g_error ("Fifo: Error reading: %s\n", err->message);
1794 parse_cmd_line(ctl_line, NULL);
1801 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1802 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1803 if (unlink(uzbl.comm.fifo_path) == -1)
1804 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1805 g_free(uzbl.comm.fifo_path);
1806 uzbl.comm.fifo_path = NULL;
1809 if (*dir == ' ') { /* space unsets the variable */
1814 GIOChannel *chan = NULL;
1815 GError *error = NULL;
1816 gchar *path = build_stream_name(FIFO, dir);
1818 if (!file_exists(path)) {
1819 if (mkfifo (path, 0666) == 0) {
1820 // 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.
1821 chan = g_io_channel_new_file(path, "r+", &error);
1823 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1824 if (uzbl.state.verbose)
1825 printf ("init_fifo: created successfully as %s\n", path);
1826 uzbl.comm.fifo_path = path;
1828 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1829 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1830 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1831 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1833 /* if we got this far, there was an error; cleanup */
1834 if (error) g_error_free (error);
1841 control_stdin(GIOChannel *gio, GIOCondition condition) {
1843 gchar *ctl_line = NULL;
1846 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1847 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1850 parse_cmd_line(ctl_line, NULL);
1858 GIOChannel *chan = NULL;
1859 GError *error = NULL;
1861 chan = g_io_channel_unix_new(fileno(stdin));
1863 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1864 g_error ("Stdin: could not add watch\n");
1866 if (uzbl.state.verbose)
1867 printf ("Stdin: watch added successfully\n");
1870 g_error ("Stdin: Error while opening: %s\n", error->message);
1872 if (error) g_error_free (error);
1876 control_socket(GIOChannel *chan) {
1877 struct sockaddr_un remote;
1878 unsigned int t = sizeof(remote);
1880 GIOChannel *clientchan;
1882 clientsock = accept (g_io_channel_unix_get_fd(chan),
1883 (struct sockaddr *) &remote, &t);
1885 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1886 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1887 (GIOFunc) control_client_socket, clientchan);
1894 control_client_socket(GIOChannel *clientchan) {
1896 GString *result = g_string_new("");
1897 GError *error = NULL;
1901 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1902 if (ret == G_IO_STATUS_ERROR) {
1903 g_warning ("Error reading: %s\n", error->message);
1904 g_io_channel_shutdown(clientchan, TRUE, &error);
1906 } else if (ret == G_IO_STATUS_EOF) {
1907 /* shutdown and remove channel watch from main loop */
1908 g_io_channel_shutdown(clientchan, TRUE, &error);
1913 parse_cmd_line (ctl_line, result);
1914 g_string_append_c(result, '\n');
1915 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1917 if (ret == G_IO_STATUS_ERROR) {
1918 g_warning ("Error writing: %s", error->message);
1920 g_io_channel_flush(clientchan, &error);
1923 if (error) g_error_free (error);
1924 g_string_free(result, TRUE);
1930 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1931 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1932 if (unlink(uzbl.comm.socket_path) == -1)
1933 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1934 g_free(uzbl.comm.socket_path);
1935 uzbl.comm.socket_path = NULL;
1943 GIOChannel *chan = NULL;
1945 struct sockaddr_un local;
1946 gchar *path = build_stream_name(SOCKET, dir);
1948 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1950 local.sun_family = AF_UNIX;
1951 strcpy (local.sun_path, path);
1952 unlink (local.sun_path);
1954 len = strlen (local.sun_path) + sizeof (local.sun_family);
1955 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1956 if (uzbl.state.verbose)
1957 printf ("init_socket: opened in %s\n", path);
1960 if( (chan = g_io_channel_unix_new(sock)) ) {
1961 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1962 uzbl.comm.socket_path = path;
1965 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1967 /* if we got this far, there was an error; cleanup */
1974 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1975 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1977 // this function may be called very early when the templates are not set (yet), hence the checks
1979 update_title (void) {
1980 Behaviour *b = &uzbl.behave;
1983 if (b->show_status) {
1984 if (b->title_format_short) {
1985 parsed = expand(b->title_format_short, 0);
1986 if (uzbl.gui.main_window)
1987 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1990 if (b->status_format) {
1991 parsed = expand(b->status_format, 0);
1992 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1995 if (b->status_background) {
1997 gdk_color_parse (b->status_background, &color);
1998 //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)
1999 if (uzbl.gui.main_window)
2000 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2001 else if (uzbl.gui.plug)
2002 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2005 if (b->title_format_long) {
2006 parsed = expand(b->title_format_long, 0);
2007 if (uzbl.gui.main_window)
2008 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2015 key_press_cb (GtkWidget* window, GdkEventKey* event)
2017 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2021 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2022 || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
2025 /* turn off insert mode (if always_insert_mode is not used) */
2026 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2027 set_insert_mode(uzbl.behave.always_insert_mode);
2032 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2035 if (event->keyval == GDK_Escape) {
2038 dehilight(uzbl.gui.web_view, NULL, NULL);
2042 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2043 if (event->keyval == GDK_Insert) {
2045 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2046 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2048 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2051 GString* keycmd = g_string_new(uzbl.state.keycmd);
2052 g_string_append (keycmd, str);
2053 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2060 if (event->keyval == GDK_BackSpace)
2061 keycmd_bs(NULL, NULL, NULL);
2063 gboolean key_ret = FALSE;
2064 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2067 GString* keycmd = g_string_new(uzbl.state.keycmd);
2068 g_string_append(keycmd, event->string);
2069 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2072 run_keycmd(key_ret);
2074 if (key_ret) return (!uzbl.behave.insert_mode);
2079 run_keycmd(const gboolean key_ret) {
2080 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2082 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2084 parse_command(act->name, act->param, NULL);
2088 /* try if it's an incremental keycmd or one that takes args, and run it */
2089 GString* short_keys = g_string_new ("");
2090 GString* short_keys_inc = g_string_new ("");
2092 guint len = strlen(uzbl.state.keycmd);
2093 for (i=0; i<len; i++) {
2094 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2095 g_string_assign(short_keys_inc, short_keys->str);
2096 g_string_append_c(short_keys, '_');
2097 g_string_append_c(short_keys_inc, '*');
2099 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2100 /* run normal cmds only if return was pressed */
2101 exec_paramcmd(act, i);
2104 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2105 if (key_ret) /* just quit the incremental command on return */
2107 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2111 g_string_truncate(short_keys, short_keys->len - 1);
2113 g_string_free (short_keys, TRUE);
2114 g_string_free (short_keys_inc, TRUE);
2118 exec_paramcmd(const Action *act, const guint i) {
2119 GString *parampart = g_string_new (uzbl.state.keycmd);
2120 GString *actionname = g_string_new ("");
2121 GString *actionparam = g_string_new ("");
2122 g_string_erase (parampart, 0, i+1);
2124 g_string_printf (actionname, act->name, parampart->str);
2126 g_string_printf (actionparam, act->param, parampart->str);
2127 parse_command(actionname->str, actionparam->str, NULL);
2128 g_string_free(actionname, TRUE);
2129 g_string_free(actionparam, TRUE);
2130 g_string_free(parampart, TRUE);
2138 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2139 //main_window_ref = g_object_ref(scrolled_window);
2140 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
2142 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2143 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2145 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2146 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2147 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2148 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2149 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2150 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2151 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2152 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2153 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2154 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2155 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2157 return scrolled_window;
2164 g->mainbar = gtk_hbox_new (FALSE, 0);
2166 /* keep a reference to the bar so we can re-pack it at runtime*/
2167 //sbar_ref = g_object_ref(g->mainbar);
2169 g->mainbar_label = gtk_label_new ("");
2170 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2171 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2172 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2173 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2174 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2175 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2180 GtkWidget* create_window () {
2181 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2182 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2183 gtk_widget_set_name (window, "Uzbl browser");
2184 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2185 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2191 GtkPlug* create_plug () {
2192 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2193 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2194 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2201 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2203 If actname is one that calls an external command, this function will inject
2204 newargs in front of the user-provided args in that command line. They will
2205 come become after the body of the script (in sh) or after the name of
2206 the command to execute (in spawn).
2207 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2208 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2210 The return value consist of two strings: the action (sh, ...) and its args.
2212 If act is not one that calls an external command, then the given action merely
2215 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2216 gchar *actdup = g_strdup(actname);
2217 g_array_append_val(rets, actdup);
2219 if ((g_strcmp0(actname, "spawn") == 0) ||
2220 (g_strcmp0(actname, "sh") == 0) ||
2221 (g_strcmp0(actname, "sync_spawn") == 0) ||
2222 (g_strcmp0(actname, "sync_sh") == 0)) {
2224 GString *a = g_string_new("");
2225 gchar **spawnparts = split_quoted(origargs, FALSE);
2226 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2227 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2229 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2230 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2232 g_array_append_val(rets, a->str);
2233 g_string_free(a, FALSE);
2234 g_strfreev(spawnparts);
2236 gchar *origdup = g_strdup(origargs);
2237 g_array_append_val(rets, origdup);
2239 return (gchar**)g_array_free(rets, FALSE);
2243 run_handler (const gchar *act, const gchar *args) {
2244 /* Consider this code a temporary hack to make the handlers usable.
2245 In practice, all this splicing, injection, and reconstruction is
2246 inefficient, annoying and hard to manage. Potential pitfalls arise
2247 when the handler specific args 1) are not quoted (the handler
2248 callbacks should take care of this) 2) are quoted but interfere
2249 with the users' own quotation. A more ideal solution is
2250 to refactor parse_command so that it doesn't just take a string
2251 and execute it; rather than that, we should have a function which
2252 returns the argument vector parsed from the string. This vector
2253 could be modified (e.g. insert additional args into it) before
2254 passing it to the next function that actually executes it. Though
2255 it still isn't perfect for chain actions.. will reconsider & re-
2256 factor when I have the time. -duc */
2258 char **parts = g_strsplit(act, " ", 2);
2260 if (g_strcmp0(parts[0], "chain") == 0) {
2261 GString *newargs = g_string_new("");
2262 gchar **chainparts = split_quoted(parts[1], FALSE);
2264 /* for every argument in the chain, inject the handler args
2265 and make sure the new parts are wrapped in quotes */
2266 gchar **cp = chainparts;
2268 gchar *quotless = NULL;
2269 gchar **spliced_quotless = NULL; // sigh -_-;
2270 gchar **inpart = NULL;
2273 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2275 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2276 } else quotless = g_strdup(*cp);
2278 spliced_quotless = g_strsplit(quotless, " ", 2);
2279 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2280 g_strfreev(spliced_quotless);
2282 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2288 parse_command(parts[0], &(newargs->str[1]), NULL);
2289 g_string_free(newargs, TRUE);
2290 g_strfreev(chainparts);
2293 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2294 parse_command(inparts[0], inparts[1], NULL);
2302 add_binding (const gchar *key, const gchar *act) {
2303 char **parts = g_strsplit(act, " ", 2);
2310 if (uzbl.state.verbose)
2311 printf ("Binding %-10s : %s\n", key, act);
2312 action = new_action(parts[0], parts[1]);
2314 if (g_hash_table_remove (uzbl.bindings, key))
2315 g_warning ("Overwriting existing binding for \"%s\"", key);
2316 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2321 get_xdg_var (XDG_Var xdg) {
2322 const gchar* actual_value = getenv (xdg.environmental);
2323 const gchar* home = getenv ("HOME");
2324 gchar* return_value;
2326 if (! actual_value || strcmp (actual_value, "") == 0) {
2327 if (xdg.default_value) {
2328 return_value = str_replace ("~", home, xdg.default_value);
2330 return_value = NULL;
2333 return_value = str_replace("~", home, actual_value);
2336 return return_value;
2340 find_xdg_file (int xdg_type, char* filename) {
2341 /* xdg_type = 0 => config
2342 xdg_type = 1 => data
2343 xdg_type = 2 => cache*/
2345 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2346 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2349 gchar* temporary_string;
2353 if (! file_exists (temporary_file) && xdg_type != 2) {
2354 buf = get_xdg_var (XDG[3 + xdg_type]);
2355 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2358 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2359 g_free (temporary_file);
2360 temporary_file = g_strconcat (temporary_string, filename, NULL);
2364 //g_free (temporary_string); - segfaults.
2366 if (file_exists (temporary_file)) {
2367 return temporary_file;
2374 State *s = &uzbl.state;
2375 Network *n = &uzbl.net;
2377 for (i = 0; default_config[i].command != NULL; i++) {
2378 parse_cmd_line(default_config[i].command, NULL);
2381 if (g_strcmp0(s->config_file, "-") == 0) {
2382 s->config_file = NULL;
2386 else if (!s->config_file) {
2387 s->config_file = find_xdg_file (0, "/uzbl/config");
2390 if (s->config_file) {
2391 GArray* lines = read_file_by_line (s->config_file);
2395 while ((line = g_array_index(lines, gchar*, i))) {
2396 parse_cmd_line (line, NULL);
2400 g_array_free (lines, TRUE);
2402 if (uzbl.state.verbose)
2403 printf ("No configuration file loaded.\n");
2406 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2409 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2412 if (!uzbl.behave.cookie_handler)
2415 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2416 GString *s = g_string_new ("");
2417 SoupURI * soup_uri = soup_message_get_uri(msg);
2418 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2419 run_handler(uzbl.behave.cookie_handler, s->str);
2421 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2422 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2423 if ( p != NULL ) *p = '\0';
2424 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2426 if (uzbl.comm.sync_stdout)
2427 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2429 g_string_free(s, TRUE);
2433 save_cookies (SoupMessage *msg, gpointer user_data){
2437 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2438 cookie = soup_cookie_to_set_cookie_header(ck->data);
2439 SoupURI * soup_uri = soup_message_get_uri(msg);
2440 GString *s = g_string_new ("");
2441 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2442 run_handler(uzbl.behave.cookie_handler, s->str);
2444 g_string_free(s, TRUE);
2449 /* --- WEBINSPECTOR --- */
2451 hide_window_cb(GtkWidget *widget, gpointer data) {
2454 gtk_widget_hide(widget);
2457 static WebKitWebView*
2458 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2461 (void) web_inspector;
2462 GtkWidget* scrolled_window;
2463 GtkWidget* new_web_view;
2466 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2467 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2468 G_CALLBACK(hide_window_cb), NULL);
2470 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2471 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2472 gtk_widget_show(g->inspector_window);
2474 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2475 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2476 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2477 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2478 gtk_widget_show(scrolled_window);
2480 new_web_view = webkit_web_view_new();
2481 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2483 return WEBKIT_WEB_VIEW(new_web_view);
2487 inspector_show_window_cb (WebKitWebInspector* inspector){
2489 gtk_widget_show(uzbl.gui.inspector_window);
2493 /* TODO: Add variables and code to make use of these functions */
2495 inspector_close_window_cb (WebKitWebInspector* inspector){
2501 inspector_attach_window_cb (WebKitWebInspector* inspector){
2507 inspector_detach_window_cb (WebKitWebInspector* inspector){
2513 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2519 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2525 set_up_inspector() {
2527 WebKitWebSettings *settings = view_settings();
2528 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2530 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2531 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2532 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2533 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2534 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2535 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2536 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2538 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2542 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2544 uzbl_cmdprop *c = v;
2549 if(c->type == TYPE_STR)
2550 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2551 else if(c->type == TYPE_INT)
2552 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2556 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2560 printf("bind %s = %s %s\n", (char *)k ,
2561 (char *)a->name, a->param?(char *)a->param:"");
2566 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2567 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2570 /* set up gtk, gobject, variable defaults and other things that tests and other
2571 * external applications need to do anyhow */
2573 initialize(int argc, char *argv[]) {
2574 gtk_init (&argc, &argv);
2575 if (!g_thread_supported ())
2576 g_thread_init (NULL);
2577 uzbl.state.executable_path = g_strdup(argv[0]);
2578 uzbl.state.selected_url = NULL;
2579 uzbl.state.searchtx = NULL;
2581 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2582 g_option_context_add_main_entries (context, entries, NULL);
2583 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2584 g_option_context_parse (context, &argc, &argv, NULL);
2585 g_option_context_free(context);
2587 if (uzbl.behave.print_version) {
2588 printf("Commit: %s\n", COMMIT);
2592 /* initialize hash table */
2593 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2595 uzbl.net.soup_session = webkit_get_default_session();
2596 uzbl.state.keycmd = g_strdup("");
2598 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2599 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2600 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2601 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2602 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2603 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2605 uzbl.gui.sbar.progress_s = g_strdup("=");
2606 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2607 uzbl.gui.sbar.progress_w = 10;
2609 /* HTML mode defaults*/
2610 uzbl.behave.html_buffer = g_string_new("");
2611 uzbl.behave.html_endmarker = g_strdup(".");
2612 uzbl.behave.html_timeout = 60;
2613 uzbl.behave.base_url = g_strdup("http://invalid");
2615 /* default mode indicators */
2616 uzbl.behave.insert_indicator = g_strdup("I");
2617 uzbl.behave.cmd_indicator = g_strdup("C");
2618 set_insert_mode(FALSE);
2620 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2621 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2622 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2623 uzbl.info.arch = ARCH;
2624 uzbl.info.commit = COMMIT;
2627 make_var_to_name_hash();
2629 uzbl.gui.scrolled_win = create_browser();
2632 #ifndef UZBL_LIBRARY
2635 main (int argc, char* argv[]) {
2636 initialize(argc, argv);
2638 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2642 /* initial packing */
2643 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2644 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2646 if (uzbl.state.socket_id) {
2647 uzbl.gui.plug = create_plug ();
2648 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2649 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2651 uzbl.gui.main_window = create_window ();
2652 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2653 gtk_widget_show_all (uzbl.gui.main_window);
2654 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2657 if(!uzbl.state.instance_name)
2658 uzbl.state.instance_name = itos((int)uzbl.xwin);
2660 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2662 if (uzbl.state.verbose) {
2663 printf("Uzbl start location: %s\n", argv[0]);
2664 if (uzbl.state.socket_id)
2665 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2667 printf("window_id %i\n",(int) uzbl.xwin);
2668 printf("pid %i\n", getpid ());
2669 printf("name: %s\n", uzbl.state.instance_name);
2672 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2673 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2674 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2675 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2676 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2678 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2679 gboolean verbose_override = uzbl.state.verbose;
2683 if (!uzbl.behave.show_status)
2684 gtk_widget_hide(uzbl.gui.mainbar);
2691 if (verbose_override > uzbl.state.verbose)
2692 uzbl.state.verbose = verbose_override;
2695 set_var_value("uri", uri_override);
2696 g_free(uri_override);
2697 } else if (uzbl.state.uri)
2698 cmd_load_uri(uzbl.gui.web_view, NULL);
2703 return EXIT_SUCCESS;
2707 /* vi: set et ts=4: */