1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
77 "Print the version and exit", NULL },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
83 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
84 the PTR() macro is kind of preventing this change at the moment. */
91 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
93 /* an abbreviation to help keep the table's width humane */
94 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* --------------------------------------------------------------------------------------- */
102 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
104 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
105 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
106 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
107 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
108 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
109 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
110 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
111 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
112 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
113 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
114 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
115 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
116 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
117 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
118 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
119 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
120 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
121 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
122 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
123 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
124 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
125 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
126 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
127 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
128 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
129 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
130 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
131 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
132 { "new_window", PTR(uzbl.behave.new_window, STR, 1, cmd_new_window)},
133 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
134 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
135 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
136 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
137 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
138 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
139 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
140 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
141 /* exported WebKitWebSettings properties */
142 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
143 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
144 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
145 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
146 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
147 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
148 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
149 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
150 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
151 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
152 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
153 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
154 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
155 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
156 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
157 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
159 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
160 }, *n2v_p = var_name_to_ptr;
166 { "SHIFT", GDK_SHIFT_MASK }, // shift
167 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
168 { "CONTROL", GDK_CONTROL_MASK }, // control
169 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
170 { "MOD2", GDK_MOD2_MASK }, // 5th mod
171 { "MOD3", GDK_MOD3_MASK }, // 6th mod
172 { "MOD4", GDK_MOD4_MASK }, // 7th mod
173 { "MOD5", GDK_MOD5_MASK }, // 8th mod
174 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
175 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
176 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
177 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
178 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
179 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
180 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
181 { "META", GDK_META_MASK }, // meta (since 2.10)
186 /* construct a hash from the var_name_to_ptr array for quick access */
188 make_var_to_name_hash() {
189 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
191 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
196 /* --- UTILITY FUNCTIONS --- */
197 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
199 get_exp_type(gchar *s) {
203 else if(*(s+1) == '{')
204 return EXP_BRACED_VAR;
205 else if(*(s+1) == '<')
208 return EXP_SIMPLE_VAR;
214 * recurse == 1: don't expand '@(command)@'
215 * recurse == 2: don't expand '@<java script>@'
218 expand(char *s, guint recurse) {
222 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
227 gchar *cmd_stdout = NULL;
229 GString *buf = g_string_new("");
230 GString *js_ret = g_string_new("");
235 g_string_append_c(buf, *++s);
240 etype = get_exp_type(s);
245 if( (vend = strpbrk(s, end_simple_var)) ||
246 (vend = strchr(s, '\0')) ) {
247 strncpy(ret, s, vend-s);
253 if( (vend = strchr(s, upto)) ||
254 (vend = strchr(s, '\0')) ) {
255 strncpy(ret, s, vend-s);
261 strcpy(str_end, ")@");
263 if( (vend = strstr(s, str_end)) ||
264 (vend = strchr(s, '\0')) ) {
265 strncpy(ret, s, vend-s);
271 strcpy(str_end, ">@");
273 if( (vend = strstr(s, str_end)) ||
274 (vend = strchr(s, '\0')) ) {
275 strncpy(ret, s, vend-s);
281 if(etype == EXP_SIMPLE_VAR ||
282 etype == EXP_BRACED_VAR) {
283 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
284 if(c->type == TYPE_STR)
285 g_string_append(buf, (gchar *)*c->ptr);
286 else if(c->type == TYPE_INT) {
287 g_string_append_printf(buf, "%d", (int)*c->ptr);
289 else if(c->type == TYPE_FLOAT) {
290 g_string_append_printf(buf, "%f", *(float *)c->ptr);
293 if(etype == EXP_SIMPLE_VAR)
298 else if(recurse != 1 &&
300 mycmd = expand(ret, 1);
301 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
305 g_printerr("error on running command: %s\n", err->message);
308 else if (*cmd_stdout) {
309 g_string_append(buf, cmd_stdout);
314 else if(recurse != 2 &&
316 mycmd = expand(ret, 2);
317 eval_js(uzbl.gui.web_view, mycmd, js_ret);
321 g_string_append(buf, js_ret->str);
322 g_string_free(js_ret, TRUE);
323 js_ret = g_string_new("");
330 g_string_append_c(buf, *s);
335 g_string_free(js_ret, TRUE);
336 return g_string_free(buf, FALSE);
343 snprintf(tmp, sizeof(tmp), "%i", val);
344 return g_strdup(tmp);
348 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
351 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
354 str_replace (const char* search, const char* replace, const char* string) {
358 buf = g_strsplit (string, search, -1);
359 ret = g_strjoinv (replace, buf);
360 g_strfreev(buf); // somebody said this segfaults
366 read_file_by_line (gchar *path) {
367 GIOChannel *chan = NULL;
368 gchar *readbuf = NULL;
370 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
373 chan = g_io_channel_new_file(path, "r", NULL);
376 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
377 const gchar* val = g_strdup (readbuf);
378 g_array_append_val (lines, val);
383 g_io_channel_unref (chan);
385 fprintf(stderr, "File '%s' not be read.\n", path);
392 parseenv (char* string) {
393 extern char** environ;
394 gchar* tmpstr = NULL;
398 while (environ[i] != NULL) {
399 gchar** env = g_strsplit (environ[i], "=", 2);
400 gchar* envname = g_strconcat ("$", env[0], NULL);
402 if (g_strrstr (string, envname) != NULL) {
403 tmpstr = g_strdup(string);
405 string = str_replace(envname, env[1], tmpstr);
410 g_strfreev (env); // somebody said this breaks uzbl
418 setup_signal(int signr, sigfunc *shandler) {
419 struct sigaction nh, oh;
421 nh.sa_handler = shandler;
422 sigemptyset(&nh.sa_mask);
425 if(sigaction(signr, &nh, &oh) < 0)
433 if (uzbl.behave.fifo_dir)
434 unlink (uzbl.comm.fifo_path);
435 if (uzbl.behave.socket_dir)
436 unlink (uzbl.comm.socket_path);
438 g_free(uzbl.state.executable_path);
439 g_string_free(uzbl.state.keycmd, TRUE);
440 g_hash_table_destroy(uzbl.bindings);
441 g_hash_table_destroy(uzbl.behave.commands);
442 g_scanner_destroy(uzbl.scan);
445 /* used for html_mode_timeout
446 * be sure to extend this function to use
447 * more timers if needed in other places
450 set_timeout(int seconds) {
452 memset(&t, 0, sizeof t);
454 t.it_value.tv_sec = seconds;
455 t.it_value.tv_usec = 0;
456 setitimer(ITIMER_REAL, &t, NULL);
459 /* --- SIGNAL HANDLER --- */
462 catch_sigterm(int s) {
468 catch_sigint(int s) {
478 set_var_value("mode", "0");
483 /* --- CALLBACKS --- */
486 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
489 (void) navigation_action;
490 (void) policy_decision;
492 const gchar* uri = webkit_network_request_get_uri (request);
493 if (uzbl.state.verbose)
494 printf("New window requested -> %s \n", uri);
495 webkit_web_policy_decision_use(policy_decision);
500 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
505 /* If we can display it, let's display it... */
506 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
507 webkit_web_policy_decision_use (policy_decision);
511 /* ...everything we can't displayed is downloaded */
512 webkit_web_policy_decision_download (policy_decision);
517 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
521 if (uzbl.state.selected_url != NULL) {
522 if (uzbl.state.verbose)
523 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
524 new_window_load_uri(uzbl.state.selected_url);
526 if (uzbl.state.verbose)
527 printf("New web view -> %s\n","Nothing to open, exiting");
533 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
536 if (uzbl.behave.download_handler) {
537 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
538 if (uzbl.state.verbose)
539 printf("Download -> %s\n",uri);
540 /* if urls not escaped, we may have to escape and quote uri before this call */
541 run_handler(uzbl.behave.download_handler, uri);
546 /* scroll a bar in a given direction */
548 scroll (GtkAdjustment* bar, GArray *argv) {
552 gdouble page_size = gtk_adjustment_get_page_size(bar);
553 gdouble value = gtk_adjustment_get_value(bar);
554 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
557 value += page_size * amount * 0.01;
561 max_value = gtk_adjustment_get_upper(bar) - page_size;
563 if (value > max_value)
564 value = max_value; /* don't scroll past the end of the page */
566 gtk_adjustment_set_value (bar, value);
570 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
571 (void) page; (void) argv; (void) result;
572 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
576 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
577 (void) page; (void) argv; (void) result;
578 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
579 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
583 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
584 (void) page; (void) result;
585 scroll(uzbl.gui.bar_v, argv);
589 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
590 (void) page; (void) result;
591 scroll(uzbl.gui.bar_h, argv);
596 if (!uzbl.behave.show_status) {
597 gtk_widget_hide(uzbl.gui.mainbar);
599 gtk_widget_show(uzbl.gui.mainbar);
605 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
610 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
614 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
619 if (uzbl.behave.show_status) {
620 gtk_widget_hide(uzbl.gui.mainbar);
622 gtk_widget_show(uzbl.gui.mainbar);
624 uzbl.behave.show_status = !uzbl.behave.show_status;
629 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
633 //Set selected_url state variable
634 g_free(uzbl.state.selected_url);
635 uzbl.state.selected_url = NULL;
637 uzbl.state.selected_url = g_strdup(link);
643 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
646 const gchar *title = webkit_web_view_get_title(web_view);
647 if (uzbl.gui.main_title)
648 g_free (uzbl.gui.main_title);
649 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
654 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
657 uzbl.gui.sbar.load_progress = progress;
662 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
666 if (uzbl.behave.load_finish_handler)
667 run_handler(uzbl.behave.load_finish_handler, "");
671 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
675 uzbl.gui.sbar.load_progress = 0;
676 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
677 if (uzbl.behave.load_start_handler)
678 run_handler(uzbl.behave.load_start_handler, "");
682 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
685 g_free (uzbl.state.uri);
686 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
687 uzbl.state.uri = g_string_free (newuri, FALSE);
688 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
689 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
692 if (uzbl.behave.load_commit_handler)
693 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
697 destroy_cb (GtkWidget* widget, gpointer data) {
705 if (uzbl.behave.history_handler) {
709 g_get_current_time(&the_time);
710 /* no need to wrap this string with quotes since it contains no spaces.
711 format is like: 2009-06-26T20:02:05.262864Z */
712 date = g_time_val_to_iso8601(&the_time);
713 run_handler(uzbl.behave.history_handler, date);
719 /* VIEW funcs (little webkit wrappers) */
720 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
722 VIEWFUNC(reload_bypass_cache)
723 VIEWFUNC(stop_loading)
730 /* -- command to callback/function map for things we cannot attach to any signals */
731 struct {char *key; CommandInfo value;} cmdlist[] =
732 { /* key function no_split */
733 { "back", {view_go_back, 0} },
734 { "forward", {view_go_forward, 0} },
735 { "scroll_vert", {scroll_vert, 0} },
736 { "scroll_horz", {scroll_horz, 0} },
737 { "scroll_begin", {scroll_begin, 0} },
738 { "scroll_end", {scroll_end, 0} },
739 { "reload", {view_reload, 0}, },
740 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
741 { "stop", {view_stop_loading, 0}, },
742 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
743 { "zoom_out", {view_zoom_out, 0}, },
744 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
745 { "uri", {load_uri, TRUE} },
746 { "js", {run_js, TRUE} },
747 { "script", {run_external_js, 0} },
748 { "toggle_status", {toggle_status_cb, 0} },
749 { "spawn", {spawn, 0} },
750 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
751 { "sh", {spawn_sh, 0} },
752 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
753 { "exit", {close_uzbl, 0} },
754 { "quit", {close_uzbl, 0} },
755 { "search", {search_forward_text, TRUE} },
756 { "search_reverse", {search_reverse_text, TRUE} },
757 { "dehilight", {dehilight, 0} },
758 { "toggle_insert_mode", {toggle_insert_mode, 0} },
759 { "set", {set_var, TRUE} },
760 //{ "get", {get_var, TRUE} },
761 { "bind", {act_bind, TRUE} },
762 { "dump_config", {act_dump_config, 0} },
763 { "keycmd", {keycmd, TRUE} },
764 { "keycmd_nl", {keycmd_nl, TRUE} },
765 { "keycmd_bs", {keycmd_bs, 0} },
766 { "chain", {chain, 0} },
767 { "print", {print, TRUE} }
774 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
776 for (i = 0; i < LENGTH(cmdlist); i++)
777 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
780 /* -- CORE FUNCTIONS -- */
783 free_action(gpointer act) {
784 Action *action = (Action*)act;
785 g_free(action->name);
787 g_free(action->param);
792 new_action(const gchar *name, const gchar *param) {
793 Action *action = g_new(Action, 1);
795 action->name = g_strdup(name);
797 action->param = g_strdup(param);
799 action->param = NULL;
805 file_exists (const char * filename) {
806 return (access(filename, F_OK) == 0);
810 set_var(WebKitWebView *page, GArray *argv, GString *result) {
811 (void) page; (void) result;
812 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
813 if (split[0] != NULL) {
814 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
815 set_var_value(g_strstrip(split[0]), value);
822 print(WebKitWebView *page, GArray *argv, GString *result) {
823 (void) page; (void) result;
826 buf = expand(argv_idx(argv, 0), 0);
827 g_string_assign(result, buf);
832 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
833 (void) page; (void) result;
834 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
835 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
836 add_binding(g_strstrip(split[0]), value);
848 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
849 (void) page; (void) result;
851 if (argv_idx(argv, 0)) {
852 if (strcmp (argv_idx(argv, 0), "0") == 0) {
853 uzbl.behave.insert_mode = FALSE;
855 uzbl.behave.insert_mode = TRUE;
858 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
865 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
868 if (argv_idx(argv, 0)) {
869 GString* newuri = g_string_new (argv_idx(argv, 0));
870 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
871 run_js(web_view, argv, NULL);
874 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
875 g_string_prepend (newuri, "http://");
876 /* if we do handle cookies, ask our handler for them */
877 webkit_web_view_load_uri (web_view, newuri->str);
878 g_string_free (newuri, TRUE);
885 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
886 size_t argumentCount, const JSValueRef arguments[],
887 JSValueRef* exception) {
892 JSStringRef js_result_string;
893 GString *result = g_string_new("");
895 if (argumentCount >= 1) {
896 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
897 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
898 char ctl_line[arg_size];
899 JSStringGetUTF8CString(arg, ctl_line, arg_size);
901 parse_cmd_line(ctl_line, result);
903 JSStringRelease(arg);
905 js_result_string = JSStringCreateWithUTF8CString(result->str);
907 g_string_free(result, TRUE);
909 return JSValueMakeString(ctx, js_result_string);
912 JSStaticFunction js_static_functions[] = {
913 {"run", js_run_command, kJSPropertyAttributeNone},
918 /* This function creates the class and its definition, only once */
919 if (!uzbl.js.initialized) {
920 /* it would be pretty cool to make this dynamic */
921 uzbl.js.classdef = kJSClassDefinitionEmpty;
922 uzbl.js.classdef.staticFunctions = js_static_functions;
924 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
930 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
931 WebKitWebFrame *frame;
932 JSGlobalContextRef context;
933 JSObjectRef globalobject;
934 JSStringRef var_name;
936 JSStringRef js_script;
937 JSValueRef js_result;
938 JSStringRef js_result_string;
939 size_t js_result_size;
943 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
944 context = webkit_web_frame_get_global_context(frame);
945 globalobject = JSContextGetGlobalObject(context);
947 /* uzbl javascript namespace */
948 var_name = JSStringCreateWithUTF8CString("Uzbl");
949 JSObjectSetProperty(context, globalobject, var_name,
950 JSObjectMake(context, uzbl.js.classref, NULL),
951 kJSClassAttributeNone, NULL);
953 /* evaluate the script and get return value*/
954 js_script = JSStringCreateWithUTF8CString(script);
955 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
956 if (js_result && !JSValueIsUndefined(context, js_result)) {
957 js_result_string = JSValueToStringCopy(context, js_result, NULL);
958 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
960 if (js_result_size) {
961 char js_result_utf8[js_result_size];
962 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
963 g_string_assign(result, js_result_utf8);
966 JSStringRelease(js_result_string);
970 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
972 JSStringRelease(var_name);
973 JSStringRelease(js_script);
977 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
978 if (argv_idx(argv, 0))
979 eval_js(web_view, argv_idx(argv, 0), result);
983 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
985 if (argv_idx(argv, 0)) {
986 GArray* lines = read_file_by_line (argv_idx (argv, 0));
991 while ((line = g_array_index(lines, gchar*, i))) {
993 js = g_strdup (line);
995 gchar* newjs = g_strconcat (js, line, NULL);
1002 if (uzbl.state.verbose)
1003 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1005 if (argv_idx (argv, 1)) {
1006 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1010 eval_js (web_view, js, result);
1012 g_array_free (lines, TRUE);
1017 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1018 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1019 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1020 webkit_web_view_unmark_text_matches (page);
1021 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1022 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1026 if (uzbl.state.searchtx) {
1027 if (uzbl.state.verbose)
1028 printf ("Searching: %s\n", uzbl.state.searchtx);
1029 webkit_web_view_set_highlight_text_matches (page, TRUE);
1030 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1035 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1037 search_text(page, argv, TRUE);
1041 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1043 search_text(page, argv, FALSE);
1047 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1048 (void) argv; (void) result;
1049 webkit_web_view_set_highlight_text_matches (page, FALSE);
1054 new_window_load_uri (const gchar * uri) {
1055 if (uzbl.behave.new_window) {
1056 GString *s = g_string_new ("");
1057 g_string_printf(s, "'%s'", uri);
1058 run_handler(uzbl.behave.new_window, s->str);
1061 GString* to_execute = g_string_new ("");
1062 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1064 for (i = 0; entries[i].long_name != NULL; i++) {
1065 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1066 gchar** str = (gchar**)entries[i].arg_data;
1068 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1072 if (uzbl.state.verbose)
1073 printf("\n%s\n", to_execute->str);
1074 g_spawn_command_line_async (to_execute->str, NULL);
1075 g_string_free (to_execute, TRUE);
1079 chain (WebKitWebView *page, GArray *argv, GString *result) {
1080 (void) page; (void) result;
1082 gchar **parts = NULL;
1084 while ((a = argv_idx(argv, i++))) {
1085 parts = g_strsplit (a, " ", 2);
1087 parse_command(parts[0], parts[1], result);
1093 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1097 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1103 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1107 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1113 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1118 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1120 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1125 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1132 /* --Statusbar functions-- */
1134 build_progressbar_ascii(int percent) {
1135 int width=uzbl.gui.sbar.progress_w;
1138 GString *bar = g_string_new("");
1140 l = (double)percent*((double)width/100.);
1141 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1143 for(i=0; i<(int)l; i++)
1144 g_string_append(bar, uzbl.gui.sbar.progress_s);
1147 g_string_append(bar, uzbl.gui.sbar.progress_u);
1149 return g_string_free(bar, FALSE);
1154 const GScannerConfig scan_config = {
1157 ) /* cset_skip_characters */,
1162 ) /* cset_identifier_first */,
1169 ) /* cset_identifier_nth */,
1170 ( "" ) /* cpair_comment_single */,
1172 TRUE /* case_sensitive */,
1174 FALSE /* skip_comment_multi */,
1175 FALSE /* skip_comment_single */,
1176 FALSE /* scan_comment_multi */,
1177 TRUE /* scan_identifier */,
1178 TRUE /* scan_identifier_1char */,
1179 FALSE /* scan_identifier_NULL */,
1180 TRUE /* scan_symbols */,
1181 FALSE /* scan_binary */,
1182 FALSE /* scan_octal */,
1183 FALSE /* scan_float */,
1184 FALSE /* scan_hex */,
1185 FALSE /* scan_hex_dollar */,
1186 FALSE /* scan_string_sq */,
1187 FALSE /* scan_string_dq */,
1188 TRUE /* numbers_2_int */,
1189 FALSE /* int_2_float */,
1190 FALSE /* identifier_2_string */,
1191 FALSE /* char_2_token */,
1192 FALSE /* symbol_2_token */,
1193 TRUE /* scope_0_fallback */,
1198 uzbl.scan = g_scanner_new(&scan_config);
1199 while(symp->symbol_name) {
1200 g_scanner_scope_add_symbol(uzbl.scan, 0,
1202 GINT_TO_POINTER(symp->symbol_token));
1208 expand_template(const char *template, gboolean escape_markup) {
1209 if(!template) return NULL;
1211 GTokenType token = G_TOKEN_NONE;
1212 GString *ret = g_string_new("");
1216 g_scanner_input_text(uzbl.scan, template, strlen(template));
1217 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1218 token = g_scanner_get_next_token(uzbl.scan);
1220 if(token == G_TOKEN_SYMBOL) {
1221 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1225 buf = uzbl.state.uri ?
1226 g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1227 g_string_append(ret, buf);
1231 g_string_append(ret, uzbl.state.uri ?
1232 uzbl.state.uri : g_strdup(""));
1235 buf = itos(uzbl.gui.sbar.load_progress);
1236 g_string_append(ret, buf);
1239 case SYM_LOADPRGSBAR:
1240 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1241 g_string_append(ret, buf);
1246 buf = uzbl.gui.main_title ?
1247 g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1248 g_string_append(ret, buf);
1252 g_string_append(ret, uzbl.gui.main_title ?
1253 uzbl.gui.main_title : "");
1255 case SYM_SELECTED_URI:
1257 buf = uzbl.state.selected_url ?
1258 g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1259 g_string_append(ret, buf);
1263 g_string_append(ret, uzbl.state.selected_url ?
1264 uzbl.state.selected_url : "");
1267 buf = itos(uzbl.xwin);
1268 g_string_append(ret,
1269 uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1274 buf = uzbl.state.keycmd->str ?
1275 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1276 g_string_append(ret, buf);
1280 g_string_append(ret, uzbl.state.keycmd->str ?
1281 uzbl.state.keycmd->str : "");
1284 g_string_append(ret,
1285 uzbl.behave.insert_mode ?
1286 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1289 g_string_append(ret,
1290 uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1292 /* useragent syms */
1294 buf = itos(WEBKIT_MAJOR_VERSION);
1295 g_string_append(ret, buf);
1299 buf = itos(WEBKIT_MINOR_VERSION);
1300 g_string_append(ret, buf);
1304 buf = itos(WEBKIT_MICRO_VERSION);
1305 g_string_append(ret, buf);
1309 g_string_append(ret, uzbl.state.unameinfo.sysname);
1312 g_string_append(ret, uzbl.state.unameinfo.nodename);
1315 g_string_append(ret, uzbl.state.unameinfo.release);
1318 g_string_append(ret, uzbl.state.unameinfo.version);
1321 g_string_append(ret, uzbl.state.unameinfo.machine);
1324 g_string_append(ret, ARCH);
1327 case SYM_DOMAINNAME:
1328 g_string_append(ret, uzbl.state.unameinfo.domainname);
1332 g_string_append(ret, COMMIT);
1338 else if(token == G_TOKEN_INT) {
1339 g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1341 else if(token == G_TOKEN_IDENTIFIER) {
1342 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1344 else if(token == G_TOKEN_CHAR) {
1345 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1347 else if(token == G_TOKEN_ERROR) {
1348 g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.",
1350 g_scanner_cur_line(uzbl.scan),
1351 g_scanner_cur_position(uzbl.scan));
1355 return g_string_free(ret, FALSE);
1357 /* --End Statusbar functions-- */
1360 sharg_append(GArray *a, const gchar *str) {
1361 const gchar *s = (str ? str : "");
1362 g_array_append_val(a, s);
1365 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1367 run_command (const gchar *command, const guint npre, const gchar **args,
1368 const gboolean sync, char **output_stdout) {
1369 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1372 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1373 gchar *pid = itos(getpid());
1374 gchar *xwin = itos(uzbl.xwin);
1376 sharg_append(a, command);
1377 for (i = 0; i < npre; i++) /* add n args before the default vars */
1378 sharg_append(a, args[i]);
1379 sharg_append(a, uzbl.state.config_file);
1380 sharg_append(a, pid);
1381 sharg_append(a, xwin);
1382 sharg_append(a, uzbl.comm.fifo_path);
1383 sharg_append(a, uzbl.comm.socket_path);
1384 sharg_append(a, uzbl.state.uri);
1385 sharg_append(a, uzbl.gui.main_title);
1387 for (i = npre; i < g_strv_length((gchar**)args); i++)
1388 sharg_append(a, args[i]);
1392 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1394 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1395 NULL, NULL, output_stdout, NULL, NULL, &err);
1396 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1397 NULL, NULL, NULL, &err);
1399 if (uzbl.state.verbose) {
1400 GString *s = g_string_new("spawned:");
1401 for (i = 0; i < (a->len); i++) {
1402 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1403 g_string_append_printf(s, " %s", qarg);
1406 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1407 printf("%s\n", s->str);
1408 g_string_free(s, TRUE);
1410 printf("Stdout: %s\n", *output_stdout);
1414 g_printerr("error on run_command: %s\n", err->message);
1419 g_array_free (a, TRUE);
1424 split_quoted(const gchar* src, const gboolean unquote) {
1425 /* split on unquoted space, return array of strings;
1426 remove a layer of quotes and backslashes if unquote */
1427 if (!src) return NULL;
1429 gboolean dq = FALSE;
1430 gboolean sq = FALSE;
1431 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1432 GString *s = g_string_new ("");
1436 for (p = src; *p != '\0'; p++) {
1437 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1438 else if (*p == '\\') { g_string_append_c(s, *p++);
1439 g_string_append_c(s, *p); }
1440 else if ((*p == '"') && unquote && !sq) dq = !dq;
1441 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1443 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1444 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1446 else if ((*p == ' ') && !dq && !sq) {
1447 dup = g_strdup(s->str);
1448 g_array_append_val(a, dup);
1449 g_string_truncate(s, 0);
1450 } else g_string_append_c(s, *p);
1452 dup = g_strdup(s->str);
1453 g_array_append_val(a, dup);
1454 ret = (gchar**)a->data;
1455 g_array_free (a, FALSE);
1456 g_string_free (s, TRUE);
1461 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1462 (void)web_view; (void)result;
1463 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1464 if (argv_idx(argv, 0))
1465 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1469 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1470 (void)web_view; (void)result;
1472 if (argv_idx(argv, 0))
1473 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1474 TRUE, &uzbl.comm.sync_stdout);
1478 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1479 (void)web_view; (void)result;
1480 if (!uzbl.behave.shell_cmd) {
1481 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1486 gchar *spacer = g_strdup("");
1487 g_array_insert_val(argv, 1, spacer);
1488 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1490 for (i = 1; i < g_strv_length(cmd); i++)
1491 g_array_prepend_val(argv, cmd[i]);
1493 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1499 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1500 (void)web_view; (void)result;
1501 if (!uzbl.behave.shell_cmd) {
1502 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1507 gchar *spacer = g_strdup("");
1508 g_array_insert_val(argv, 1, spacer);
1509 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1511 for (i = 1; i < g_strv_length(cmd); i++)
1512 g_array_prepend_val(argv, cmd[i]);
1514 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1515 TRUE, &uzbl.comm.sync_stdout);
1521 parse_command(const char *cmd, const char *param, GString *result) {
1524 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1526 gchar **par = split_quoted(param, TRUE);
1527 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1529 if (c->no_split) { /* don't split */
1530 sharg_append(a, param);
1532 for (i = 0; i < g_strv_length(par); i++)
1533 sharg_append(a, par[i]);
1536 if (result == NULL) {
1537 GString *result_print = g_string_new("");
1539 c->function(uzbl.gui.web_view, a, result_print);
1540 if (result_print->len)
1541 printf("%*s\n", result_print->len, result_print->str);
1543 g_string_free(result_print, TRUE);
1545 c->function(uzbl.gui.web_view, a, result);
1548 g_array_free (a, TRUE);
1551 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1558 if(*uzbl.net.proxy_url == ' '
1559 || uzbl.net.proxy_url == NULL) {
1560 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1561 (GType) SOUP_SESSION_PROXY_URI);
1564 suri = soup_uri_new(uzbl.net.proxy_url);
1565 g_object_set(G_OBJECT(uzbl.net.soup_session),
1566 SOUP_SESSION_PROXY_URI,
1568 soup_uri_free(suri);
1575 if(file_exists(uzbl.gui.icon)) {
1576 if (uzbl.gui.main_window)
1577 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1579 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1585 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1586 g_array_append_val (a, uzbl.state.uri);
1587 load_uri(uzbl.gui.web_view, a, NULL);
1588 g_array_free (a, TRUE);
1592 cmd_always_insert_mode() {
1593 uzbl.behave.insert_mode =
1594 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1600 g_object_set(G_OBJECT(uzbl.net.soup_session),
1601 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1605 cmd_max_conns_host() {
1606 g_object_set(G_OBJECT(uzbl.net.soup_session),
1607 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1612 soup_session_remove_feature
1613 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1614 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1615 /*g_free(uzbl.net.soup_logger);*/
1617 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1618 soup_session_add_feature(uzbl.net.soup_session,
1619 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1624 return webkit_web_view_get_settings(uzbl.gui.web_view);
1629 WebKitWebSettings *ws = view_settings();
1630 if (uzbl.behave.font_size > 0) {
1631 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1634 if (uzbl.behave.monospace_size > 0) {
1635 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1636 uzbl.behave.monospace_size, NULL);
1638 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1639 uzbl.behave.font_size, NULL);
1645 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1649 cmd_disable_plugins() {
1650 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1651 !uzbl.behave.disable_plugins, NULL);
1655 cmd_disable_scripts() {
1656 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1657 !uzbl.behave.disable_scripts, NULL);
1661 cmd_minimum_font_size() {
1662 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1663 uzbl.behave.minimum_font_size, NULL);
1666 cmd_autoload_img() {
1667 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1668 uzbl.behave.autoload_img, NULL);
1673 cmd_autoshrink_img() {
1674 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1675 uzbl.behave.autoshrink_img, NULL);
1680 cmd_enable_spellcheck() {
1681 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1682 uzbl.behave.enable_spellcheck, NULL);
1686 cmd_enable_private() {
1687 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1688 uzbl.behave.enable_private, NULL);
1693 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1694 uzbl.behave.print_bg, NULL);
1699 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1700 uzbl.behave.style_uri, NULL);
1704 cmd_resizable_txt() {
1705 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1706 uzbl.behave.resizable_txt, NULL);
1710 cmd_default_encoding() {
1711 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1712 uzbl.behave.default_encoding, NULL);
1716 cmd_enforce_96dpi() {
1717 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1718 uzbl.behave.enforce_96dpi, NULL);
1722 cmd_caret_browsing() {
1723 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1724 uzbl.behave.caret_browsing, NULL);
1728 cmd_cookie_handler() {
1729 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1730 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1731 if ((g_strcmp0(split[0], "sh") == 0) ||
1732 (g_strcmp0(split[0], "spawn") == 0)) {
1733 g_free (uzbl.behave.cookie_handler);
1734 uzbl.behave.cookie_handler =
1735 g_strdup_printf("sync_%s %s", split[0], split[1]);
1742 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1743 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1744 if ((g_strcmp0(split[0], "sh") == 0) ||
1745 (g_strcmp0(split[0], "spawn") == 0)) {
1746 g_free (uzbl.behave.new_window);
1747 uzbl.behave.new_window =
1748 g_strdup_printf("%s %s", split[0], split[1]);
1755 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1760 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1765 if(uzbl.behave.inject_html) {
1766 webkit_web_view_load_html_string (uzbl.gui.web_view,
1767 uzbl.behave.inject_html, NULL);
1776 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1777 uzbl.behave.modmask = 0;
1779 if(uzbl.behave.modkey)
1780 g_free(uzbl.behave.modkey);
1781 uzbl.behave.modkey = buf;
1783 for (i = 0; modkeys[i].key != NULL; i++) {
1784 if (g_strrstr(buf, modkeys[i].key))
1785 uzbl.behave.modmask |= modkeys[i].mask;
1791 if (*uzbl.net.useragent == ' ') {
1792 g_free (uzbl.net.useragent);
1793 uzbl.net.useragent = NULL;
1795 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1797 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1798 g_free(uzbl.net.useragent);
1799 uzbl.net.useragent = ua;
1805 gtk_widget_ref(uzbl.gui.scrolled_win);
1806 gtk_widget_ref(uzbl.gui.mainbar);
1807 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1808 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1810 if(uzbl.behave.status_top) {
1811 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1812 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1815 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1816 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1818 gtk_widget_unref(uzbl.gui.scrolled_win);
1819 gtk_widget_unref(uzbl.gui.mainbar);
1820 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1825 set_var_value(gchar *name, gchar *val) {
1826 uzbl_cmdprop *c = NULL;
1830 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1831 /* check for the variable type */
1832 if (c->type == TYPE_STR) {
1833 buf = expand(val, 0);
1836 } else if(c->type == TYPE_INT) {
1837 int *ip = (int *)c->ptr;
1838 buf = expand(val, 0);
1839 *ip = (int)strtoul(buf, &endp, 10);
1841 } else if (c->type == TYPE_FLOAT) {
1842 float *fp = (float *)c->ptr;
1843 buf = expand(val, 0);
1844 *fp = strtod(buf, &endp);
1848 /* invoke a command specific function */
1849 if(c->func) c->func();
1856 Behaviour *b = &uzbl.behave;
1858 if(b->html_buffer->str) {
1859 webkit_web_view_load_html_string (uzbl.gui.web_view,
1860 b->html_buffer->str, b->base_url);
1861 g_string_free(b->html_buffer, TRUE);
1862 b->html_buffer = g_string_new("");
1866 enum {M_CMD, M_HTML};
1868 parse_cmd_line(const char *ctl_line, GString *result) {
1869 Behaviour *b = &uzbl.behave;
1872 if(b->mode == M_HTML) {
1873 len = strlen(b->html_endmarker);
1874 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1875 if(len == strlen(ctl_line)-1 &&
1876 !strncmp(b->html_endmarker, ctl_line, len)) {
1878 set_var_value("mode", "0");
1883 set_timeout(b->html_timeout);
1884 g_string_append(b->html_buffer, ctl_line);
1887 else if((ctl_line[0] == '#') /* Comments */
1888 || (ctl_line[0] == ' ')
1889 || (ctl_line[0] == '\n'))
1890 ; /* ignore these lines */
1891 else { /* parse a command */
1893 gchar **tokens = NULL;
1894 len = strlen(ctl_line);
1896 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1897 ctlstrip = g_strndup(ctl_line, len - 1);
1898 else ctlstrip = g_strdup(ctl_line);
1900 tokens = g_strsplit(ctlstrip, " ", 2);
1901 parse_command(tokens[0], tokens[1], result);
1908 build_stream_name(int type, const gchar* dir) {
1909 char *xwin_str = NULL;
1910 State *s = &uzbl.state;
1913 xwin_str = itos((int)uzbl.xwin);
1915 str = g_strdup_printf
1916 ("%s/uzbl_fifo_%s", dir,
1917 s->instance_name ? s->instance_name : xwin_str);
1918 } else if (type == SOCKET) {
1919 str = g_strdup_printf
1920 ("%s/uzbl_socket_%s", dir,
1921 s->instance_name ? s->instance_name : xwin_str );
1928 control_fifo(GIOChannel *gio, GIOCondition condition) {
1929 if (uzbl.state.verbose)
1930 printf("triggered\n");
1935 if (condition & G_IO_HUP)
1936 g_error ("Fifo: Read end of pipe died!\n");
1939 g_error ("Fifo: GIOChannel broke\n");
1941 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1942 if (ret == G_IO_STATUS_ERROR) {
1943 g_error ("Fifo: Error reading: %s\n", err->message);
1947 parse_cmd_line(ctl_line, NULL);
1954 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1955 GIOChannel *chan = NULL;
1956 GError *error = NULL;
1957 gchar *path = build_stream_name(FIFO, dir);
1959 if (!file_exists(path)) {
1960 if (mkfifo (path, 0666) == 0) {
1961 // 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.
1962 chan = g_io_channel_new_file(path, "r+", &error);
1964 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1965 if (uzbl.state.verbose)
1966 printf ("init_fifo: created successfully as %s\n", path);
1967 uzbl.comm.fifo_path = path;
1969 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1970 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1971 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1972 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1974 /* if we got this far, there was an error; cleanup */
1975 if (error) g_error_free (error);
1982 control_stdin(GIOChannel *gio, GIOCondition condition) {
1984 gchar *ctl_line = NULL;
1987 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1988 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1991 parse_cmd_line(ctl_line, NULL);
1999 GIOChannel *chan = NULL;
2000 GError *error = NULL;
2002 chan = g_io_channel_unix_new(fileno(stdin));
2004 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2005 g_error ("Stdin: could not add watch\n");
2007 if (uzbl.state.verbose)
2008 printf ("Stdin: watch added successfully\n");
2011 g_error ("Stdin: Error while opening: %s\n", error->message);
2013 if (error) g_error_free (error);
2017 control_socket(GIOChannel *chan) {
2018 struct sockaddr_un remote;
2019 unsigned int t = sizeof(remote);
2021 GIOChannel *clientchan;
2023 clientsock = accept (g_io_channel_unix_get_fd(chan),
2024 (struct sockaddr *) &remote, &t);
2026 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2027 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2028 (GIOFunc) control_client_socket, clientchan);
2035 control_client_socket(GIOChannel *clientchan) {
2037 GString *result = g_string_new("");
2038 GError *error = NULL;
2042 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2043 if (ret == G_IO_STATUS_ERROR) {
2044 g_warning ("Error reading: %s\n", error->message);
2045 g_io_channel_shutdown(clientchan, TRUE, &error);
2047 } else if (ret == G_IO_STATUS_EOF) {
2048 /* shutdown and remove channel watch from main loop */
2049 g_io_channel_shutdown(clientchan, TRUE, &error);
2054 parse_cmd_line (ctl_line, result);
2055 g_string_append_c(result, '\n');
2056 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2058 if (ret == G_IO_STATUS_ERROR) {
2059 g_warning ("Error writing: %s", error->message);
2061 g_io_channel_flush(clientchan, &error);
2064 if (error) g_error_free (error);
2065 g_string_free(result, TRUE);
2071 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2072 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2073 if (unlink(uzbl.comm.socket_path) == -1)
2074 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2075 g_free(uzbl.comm.socket_path);
2076 uzbl.comm.socket_path = NULL;
2084 GIOChannel *chan = NULL;
2086 struct sockaddr_un local;
2087 gchar *path = build_stream_name(SOCKET, dir);
2089 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2091 local.sun_family = AF_UNIX;
2092 strcpy (local.sun_path, path);
2093 unlink (local.sun_path);
2095 len = strlen (local.sun_path) + sizeof (local.sun_family);
2096 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2097 if (uzbl.state.verbose)
2098 printf ("init_socket: opened in %s\n", path);
2101 if( (chan = g_io_channel_unix_new(sock)) ) {
2102 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2103 uzbl.comm.socket_path = path;
2106 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2108 /* if we got this far, there was an error; cleanup */
2115 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2116 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2118 // this function may be called very early when the templates are not set (yet), hence the checks
2120 update_title (void) {
2121 Behaviour *b = &uzbl.behave;
2124 if (b->show_status) {
2125 if (b->title_format_short) {
2126 parsed = expand_template(b->title_format_short, FALSE);
2127 if (uzbl.gui.main_window)
2128 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2131 if (b->status_format) {
2132 parsed = expand_template(b->status_format, TRUE);
2133 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2136 if (b->status_background) {
2138 gdk_color_parse (b->status_background, &color);
2139 //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)
2140 if (uzbl.gui.main_window)
2141 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2142 else if (uzbl.gui.plug)
2143 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2146 if (b->title_format_long) {
2147 parsed = expand_template(b->title_format_long, FALSE);
2148 if (uzbl.gui.main_window)
2149 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2156 key_press_cb (GtkWidget* window, GdkEventKey* event)
2158 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2162 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2163 || 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)
2166 /* turn off insert mode (if always_insert_mode is not used) */
2167 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2168 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2173 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2176 if (event->keyval == GDK_Escape) {
2177 g_string_truncate(uzbl.state.keycmd, 0);
2179 dehilight(uzbl.gui.web_view, NULL, NULL);
2183 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2184 if (event->keyval == GDK_Insert) {
2186 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2187 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2189 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2192 g_string_append (uzbl.state.keycmd, str);
2199 if (event->keyval == GDK_BackSpace)
2200 keycmd_bs(NULL, NULL, NULL);
2202 gboolean key_ret = FALSE;
2203 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2205 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2207 run_keycmd(key_ret);
2209 if (key_ret) return (!uzbl.behave.insert_mode);
2214 run_keycmd(const gboolean key_ret) {
2215 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2217 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2218 g_string_truncate(uzbl.state.keycmd, 0);
2219 parse_command(act->name, act->param, NULL);
2223 /* try if it's an incremental keycmd or one that takes args, and run it */
2224 GString* short_keys = g_string_new ("");
2225 GString* short_keys_inc = g_string_new ("");
2227 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2228 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2229 g_string_assign(short_keys_inc, short_keys->str);
2230 g_string_append_c(short_keys, '_');
2231 g_string_append_c(short_keys_inc, '*');
2233 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2234 /* run normal cmds only if return was pressed */
2235 exec_paramcmd(act, i);
2236 g_string_truncate(uzbl.state.keycmd, 0);
2238 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2239 if (key_ret) /* just quit the incremental command on return */
2240 g_string_truncate(uzbl.state.keycmd, 0);
2241 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2245 g_string_truncate(short_keys, short_keys->len - 1);
2247 g_string_free (short_keys, TRUE);
2248 g_string_free (short_keys_inc, TRUE);
2252 exec_paramcmd(const Action *act, const guint i) {
2253 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2254 GString *actionname = g_string_new ("");
2255 GString *actionparam = g_string_new ("");
2256 g_string_erase (parampart, 0, i+1);
2258 g_string_printf (actionname, act->name, parampart->str);
2260 g_string_printf (actionparam, act->param, parampart->str);
2261 parse_command(actionname->str, actionparam->str, NULL);
2262 g_string_free(actionname, TRUE);
2263 g_string_free(actionparam, TRUE);
2264 g_string_free(parampart, TRUE);
2272 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2273 //main_window_ref = g_object_ref(scrolled_window);
2274 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
2276 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2277 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2279 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2280 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2281 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2282 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2283 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2284 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2285 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2286 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2287 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2288 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2289 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2291 return scrolled_window;
2298 g->mainbar = gtk_hbox_new (FALSE, 0);
2300 /* keep a reference to the bar so we can re-pack it at runtime*/
2301 //sbar_ref = g_object_ref(g->mainbar);
2303 g->mainbar_label = gtk_label_new ("");
2304 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2305 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2306 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2307 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2308 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2309 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2315 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2316 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2317 gtk_widget_set_name (window, "Uzbl browser");
2318 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2319 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2326 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2327 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2328 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2335 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2337 If actname is one that calls an external command, this function will inject
2338 newargs in front of the user-provided args in that command line. They will
2339 come become after the body of the script (in sh) or after the name of
2340 the command to execute (in spawn).
2341 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2342 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2344 The return value consist of two strings: the action (sh, ...) and its args.
2346 If act is not one that calls an external command, then the given action merely
2349 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2350 /* Arrr! Here be memory leaks */
2351 gchar *actdup = g_strdup(actname);
2352 g_array_append_val(rets, actdup);
2354 if ((g_strcmp0(actname, "spawn") == 0) ||
2355 (g_strcmp0(actname, "sh") == 0) ||
2356 (g_strcmp0(actname, "sync_spawn") == 0) ||
2357 (g_strcmp0(actname, "sync_sh") == 0)) {
2359 GString *a = g_string_new("");
2360 gchar **spawnparts = split_quoted(origargs, FALSE);
2361 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2362 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2364 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2365 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2367 g_array_append_val(rets, a->str);
2368 g_string_free(a, FALSE);
2369 g_strfreev(spawnparts);
2371 gchar *origdup = g_strdup(origargs);
2372 g_array_append_val(rets, origdup);
2374 return (gchar**)g_array_free(rets, FALSE);
2378 run_handler (const gchar *act, const gchar *args) {
2379 /* Consider this code a temporary hack to make the handlers usable.
2380 In practice, all this splicing, injection, and reconstruction is
2381 inefficient, annoying and hard to manage. Potential pitfalls arise
2382 when the handler specific args 1) are not quoted (the handler
2383 callbacks should take care of this) 2) are quoted but interfere
2384 with the users' own quotation. A more ideal solution is
2385 to refactor parse_command so that it doesn't just take a string
2386 and execute it; rather than that, we should have a function which
2387 returns the argument vector parsed from the string. This vector
2388 could be modified (e.g. insert additional args into it) before
2389 passing it to the next function that actually executes it. Though
2390 it still isn't perfect for chain actions.. will reconsider & re-
2391 factor when I have the time. -duc */
2393 char **parts = g_strsplit(act, " ", 2);
2395 if (g_strcmp0(parts[0], "chain") == 0) {
2396 GString *newargs = g_string_new("");
2397 gchar **chainparts = split_quoted(parts[1], FALSE);
2399 /* for every argument in the chain, inject the handler args
2400 and make sure the new parts are wrapped in quotes */
2401 gchar **cp = chainparts;
2403 gchar *quotless = NULL;
2404 gchar **spliced_quotless = NULL; // sigh -_-;
2405 gchar **inpart = NULL;
2408 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2410 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2411 } else quotless = g_strdup(*cp);
2413 spliced_quotless = g_strsplit(quotless, " ", 2);
2414 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2415 g_strfreev(spliced_quotless);
2417 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2423 parse_command(parts[0], &(newargs->str[1]), NULL);
2424 g_string_free(newargs, TRUE);
2425 g_strfreev(chainparts);
2428 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2429 parse_command(inparts[0], inparts[1], NULL);
2437 add_binding (const gchar *key, const gchar *act) {
2438 char **parts = g_strsplit(act, " ", 2);
2445 if (uzbl.state.verbose)
2446 printf ("Binding %-10s : %s\n", key, act);
2447 action = new_action(parts[0], parts[1]);
2449 if (g_hash_table_remove (uzbl.bindings, key))
2450 g_warning ("Overwriting existing binding for \"%s\"", key);
2451 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2456 get_xdg_var (XDG_Var xdg) {
2457 const gchar* actual_value = getenv (xdg.environmental);
2458 const gchar* home = getenv ("HOME");
2459 gchar* return_value;
2461 if (! actual_value || strcmp (actual_value, "") == 0) {
2462 if (xdg.default_value) {
2463 return_value = str_replace ("~", home, xdg.default_value);
2465 return_value = NULL;
2468 return_value = str_replace("~", home, actual_value);
2471 return return_value;
2475 find_xdg_file (int xdg_type, char* filename) {
2476 /* xdg_type = 0 => config
2477 xdg_type = 1 => data
2478 xdg_type = 2 => cache*/
2480 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2481 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2484 gchar* temporary_string;
2488 if (! file_exists (temporary_file) && xdg_type != 2) {
2489 buf = get_xdg_var (XDG[3 + xdg_type]);
2490 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2493 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2494 g_free (temporary_file);
2495 temporary_file = g_strconcat (temporary_string, filename, NULL);
2499 //g_free (temporary_string); - segfaults.
2501 if (file_exists (temporary_file)) {
2502 return temporary_file;
2509 State *s = &uzbl.state;
2510 Network *n = &uzbl.net;
2512 for (i = 0; default_config[i].command != NULL; i++) {
2513 parse_cmd_line(default_config[i].command, NULL);
2516 if (g_strcmp0(s->config_file, "-") == 0) {
2517 s->config_file = NULL;
2521 else if (!s->config_file) {
2522 s->config_file = find_xdg_file (0, "/uzbl/config");
2525 if (s->config_file) {
2526 GArray* lines = read_file_by_line (s->config_file);
2530 while ((line = g_array_index(lines, gchar*, i))) {
2531 parse_cmd_line (line, NULL);
2535 g_array_free (lines, TRUE);
2537 if (uzbl.state.verbose)
2538 printf ("No configuration file loaded.\n");
2541 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2544 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2547 if (!uzbl.behave.cookie_handler)
2550 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2551 GString *s = g_string_new ("");
2552 SoupURI * soup_uri = soup_message_get_uri(msg);
2553 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2554 run_handler(uzbl.behave.cookie_handler, s->str);
2556 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2557 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2558 if ( p != NULL ) *p = '\0';
2559 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2561 if (uzbl.comm.sync_stdout)
2562 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2564 g_string_free(s, TRUE);
2568 save_cookies (SoupMessage *msg, gpointer user_data){
2572 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2573 cookie = soup_cookie_to_set_cookie_header(ck->data);
2574 SoupURI * soup_uri = soup_message_get_uri(msg);
2575 GString *s = g_string_new ("");
2576 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2577 run_handler(uzbl.behave.cookie_handler, s->str);
2579 g_string_free(s, TRUE);
2584 /* --- WEBINSPECTOR --- */
2586 hide_window_cb(GtkWidget *widget, gpointer data) {
2589 gtk_widget_hide(widget);
2593 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2596 (void) web_inspector;
2597 GtkWidget* scrolled_window;
2598 GtkWidget* new_web_view;
2601 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2602 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2603 G_CALLBACK(hide_window_cb), NULL);
2605 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2606 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2607 gtk_widget_show(g->inspector_window);
2609 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2610 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2611 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2612 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2613 gtk_widget_show(scrolled_window);
2615 new_web_view = webkit_web_view_new();
2616 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2618 return WEBKIT_WEB_VIEW(new_web_view);
2622 inspector_show_window_cb (WebKitWebInspector* inspector){
2624 gtk_widget_show(uzbl.gui.inspector_window);
2628 /* TODO: Add variables and code to make use of these functions */
2630 inspector_close_window_cb (WebKitWebInspector* inspector){
2636 inspector_attach_window_cb (WebKitWebInspector* inspector){
2642 inspector_detach_window_cb (WebKitWebInspector* inspector){
2648 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2654 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2660 set_up_inspector() {
2662 WebKitWebSettings *settings = view_settings();
2663 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2665 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2666 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2667 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2668 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2669 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2670 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2671 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2673 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2677 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2679 uzbl_cmdprop *c = v;
2684 if(c->type == TYPE_STR)
2685 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2686 else if(c->type == TYPE_INT)
2687 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2688 else if(c->type == TYPE_FLOAT)
2689 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2693 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2697 printf("bind %s = %s %s\n", (char *)k ,
2698 (char *)a->name, a->param?(char *)a->param:"");
2703 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2704 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2707 #ifndef UZBL_LIBRARY
2710 main (int argc, char* argv[]) {
2711 gtk_init (&argc, &argv);
2712 if (!g_thread_supported ())
2713 g_thread_init (NULL);
2714 uzbl.state.executable_path = g_strdup(argv[0]);
2715 uzbl.state.selected_url = NULL;
2716 uzbl.state.searchtx = NULL;
2718 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2719 g_option_context_add_main_entries (context, entries, NULL);
2720 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2721 g_option_context_parse (context, &argc, &argv, NULL);
2722 g_option_context_free(context);
2724 if (uzbl.behave.print_version) {
2725 printf("Commit: %s\n", COMMIT);
2729 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2730 if (argc > 1 && !uzbl.state.uri)
2731 uri_override = g_strdup(argv[1]);
2732 gboolean verbose_override = uzbl.state.verbose;
2734 /* initialize hash table */
2735 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2737 uzbl.net.soup_session = webkit_get_default_session();
2738 uzbl.state.keycmd = g_string_new("");
2740 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2741 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2742 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2743 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2744 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2745 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2748 if(uname(&uzbl.state.unameinfo) == -1)
2749 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2751 uzbl.gui.sbar.progress_s = g_strdup("=");
2752 uzbl.gui.sbar.progress_u = g_strdup("·");
2753 uzbl.gui.sbar.progress_w = 10;
2755 /* HTML mode defaults*/
2756 uzbl.behave.html_buffer = g_string_new("");
2757 uzbl.behave.html_endmarker = g_strdup(".");
2758 uzbl.behave.html_timeout = 60;
2759 uzbl.behave.base_url = g_strdup("http://invalid");
2761 /* default mode indicators */
2762 uzbl.behave.insert_indicator = g_strdup("I");
2763 uzbl.behave.cmd_indicator = g_strdup("C");
2767 make_var_to_name_hash();
2769 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2771 uzbl.gui.scrolled_win = create_browser();
2774 /* initial packing */
2775 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2776 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2778 if (uzbl.state.socket_id) {
2779 uzbl.gui.plug = create_plug ();
2780 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2781 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2783 uzbl.gui.main_window = create_window ();
2784 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2785 gtk_widget_show_all (uzbl.gui.main_window);
2786 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2789 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2791 if (uzbl.state.verbose) {
2792 printf("Uzbl start location: %s\n", argv[0]);
2793 if (uzbl.state.socket_id)
2794 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2796 printf("window_id %i\n",(int) uzbl.xwin);
2797 printf("pid %i\n", getpid ());
2798 printf("name: %s\n", uzbl.state.instance_name);
2801 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2802 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2803 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2804 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2805 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2809 if (!uzbl.behave.show_status)
2810 gtk_widget_hide(uzbl.gui.mainbar);
2817 if (verbose_override > uzbl.state.verbose)
2818 uzbl.state.verbose = verbose_override;
2821 set_var_value("uri", uri_override);
2822 g_free(uri_override);
2823 } else if (uzbl.state.uri)
2824 cmd_load_uri(uzbl.gui.web_view, NULL);
2829 return EXIT_SUCCESS;
2833 /* vi: set et ts=4: */