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 { "search", {search_forward_text, TRUE} },
755 { "search_reverse", {search_reverse_text, TRUE} },
756 { "dehilight", {dehilight, 0} },
757 { "toggle_insert_mode", {toggle_insert_mode, 0} },
758 { "set", {set_var, TRUE} },
759 //{ "get", {get_var, TRUE} },
760 { "bind", {act_bind, TRUE} },
761 { "dump_config", {act_dump_config, 0} },
762 { "keycmd", {keycmd, TRUE} },
763 { "keycmd_nl", {keycmd_nl, TRUE} },
764 { "keycmd_bs", {keycmd_bs, 0} },
765 { "chain", {chain, 0} },
766 { "print", {print, TRUE} }
773 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
775 for (i = 0; i < LENGTH(cmdlist); i++)
776 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
779 /* -- CORE FUNCTIONS -- */
782 free_action(gpointer act) {
783 Action *action = (Action*)act;
784 g_free(action->name);
786 g_free(action->param);
791 new_action(const gchar *name, const gchar *param) {
792 Action *action = g_new(Action, 1);
794 action->name = g_strdup(name);
796 action->param = g_strdup(param);
798 action->param = NULL;
804 file_exists (const char * filename) {
805 return (access(filename, F_OK) == 0);
809 set_var(WebKitWebView *page, GArray *argv, GString *result) {
810 (void) page; (void) result;
811 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
812 if (split[0] != NULL) {
813 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
814 set_var_value(g_strstrip(split[0]), value);
821 print(WebKitWebView *page, GArray *argv, GString *result) {
822 (void) page; (void) result;
825 buf = expand(argv_idx(argv, 0), 0);
826 g_string_assign(result, buf);
831 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
832 (void) page; (void) result;
833 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
834 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
835 add_binding(g_strstrip(split[0]), value);
847 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
848 (void) page; (void) result;
850 if (argv_idx(argv, 0)) {
851 if (strcmp (argv_idx(argv, 0), "0") == 0) {
852 uzbl.behave.insert_mode = FALSE;
854 uzbl.behave.insert_mode = TRUE;
857 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
864 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
867 if (argv_idx(argv, 0)) {
868 GString* newuri = g_string_new (argv_idx(argv, 0));
869 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
870 run_js(web_view, argv, NULL);
873 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
874 g_string_prepend (newuri, "http://");
875 /* if we do handle cookies, ask our handler for them */
876 webkit_web_view_load_uri (web_view, newuri->str);
877 g_string_free (newuri, TRUE);
884 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
885 size_t argumentCount, const JSValueRef arguments[],
886 JSValueRef* exception) {
891 JSStringRef js_result_string;
892 GString *result = g_string_new("");
894 if (argumentCount >= 1) {
895 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
896 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
897 char ctl_line[arg_size];
898 JSStringGetUTF8CString(arg, ctl_line, arg_size);
900 parse_cmd_line(ctl_line, result);
902 JSStringRelease(arg);
904 js_result_string = JSStringCreateWithUTF8CString(result->str);
906 g_string_free(result, TRUE);
908 return JSValueMakeString(ctx, js_result_string);
911 JSStaticFunction js_static_functions[] = {
912 {"run", js_run_command, kJSPropertyAttributeNone},
917 /* This function creates the class and its definition, only once */
918 if (!uzbl.js.initialized) {
919 /* it would be pretty cool to make this dynamic */
920 uzbl.js.classdef = kJSClassDefinitionEmpty;
921 uzbl.js.classdef.staticFunctions = js_static_functions;
923 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
929 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
930 WebKitWebFrame *frame;
931 JSGlobalContextRef context;
932 JSObjectRef globalobject;
933 JSStringRef var_name;
935 JSStringRef js_script;
936 JSValueRef js_result;
937 JSStringRef js_result_string;
938 size_t js_result_size;
942 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
943 context = webkit_web_frame_get_global_context(frame);
944 globalobject = JSContextGetGlobalObject(context);
946 /* uzbl javascript namespace */
947 var_name = JSStringCreateWithUTF8CString("Uzbl");
948 JSObjectSetProperty(context, globalobject, var_name,
949 JSObjectMake(context, uzbl.js.classref, NULL),
950 kJSClassAttributeNone, NULL);
952 /* evaluate the script and get return value*/
953 js_script = JSStringCreateWithUTF8CString(script);
954 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
955 if (js_result && !JSValueIsUndefined(context, js_result)) {
956 js_result_string = JSValueToStringCopy(context, js_result, NULL);
957 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
959 if (js_result_size) {
960 char js_result_utf8[js_result_size];
961 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
962 g_string_assign(result, js_result_utf8);
965 JSStringRelease(js_result_string);
969 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
971 JSStringRelease(var_name);
972 JSStringRelease(js_script);
976 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
977 if (argv_idx(argv, 0))
978 eval_js(web_view, argv_idx(argv, 0), result);
982 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
984 if (argv_idx(argv, 0)) {
985 GArray* lines = read_file_by_line (argv_idx (argv, 0));
990 while ((line = g_array_index(lines, gchar*, i))) {
992 js = g_strdup (line);
994 gchar* newjs = g_strconcat (js, line, NULL);
1001 if (uzbl.state.verbose)
1002 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1004 if (argv_idx (argv, 1)) {
1005 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1009 eval_js (web_view, js, result);
1011 g_array_free (lines, TRUE);
1016 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1017 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1018 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1019 webkit_web_view_unmark_text_matches (page);
1020 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1021 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1025 if (uzbl.state.searchtx) {
1026 if (uzbl.state.verbose)
1027 printf ("Searching: %s\n", uzbl.state.searchtx);
1028 webkit_web_view_set_highlight_text_matches (page, TRUE);
1029 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1034 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1036 search_text(page, argv, TRUE);
1040 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1042 search_text(page, argv, FALSE);
1046 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1047 (void) argv; (void) result;
1048 webkit_web_view_set_highlight_text_matches (page, FALSE);
1053 new_window_load_uri (const gchar * uri) {
1054 if (uzbl.behave.new_window) {
1055 GString *s = g_string_new ("");
1056 g_string_printf(s, "'%s'", uri);
1057 run_handler(uzbl.behave.new_window, s->str);
1060 GString* to_execute = g_string_new ("");
1061 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1063 for (i = 0; entries[i].long_name != NULL; i++) {
1064 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1065 gchar** str = (gchar**)entries[i].arg_data;
1067 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1071 if (uzbl.state.verbose)
1072 printf("\n%s\n", to_execute->str);
1073 g_spawn_command_line_async (to_execute->str, NULL);
1074 g_string_free (to_execute, TRUE);
1078 chain (WebKitWebView *page, GArray *argv, GString *result) {
1079 (void) page; (void) result;
1081 gchar **parts = NULL;
1083 while ((a = argv_idx(argv, i++))) {
1084 parts = g_strsplit (a, " ", 2);
1086 parse_command(parts[0], parts[1], result);
1092 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1096 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1102 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1106 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1112 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1117 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1119 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1124 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1131 /* --Statusbar functions-- */
1133 build_progressbar_ascii(int percent) {
1134 int width=uzbl.gui.sbar.progress_w;
1137 GString *bar = g_string_new("");
1139 l = (double)percent*((double)width/100.);
1140 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1142 for(i=0; i<(int)l; i++)
1143 g_string_append(bar, uzbl.gui.sbar.progress_s);
1146 g_string_append(bar, uzbl.gui.sbar.progress_u);
1148 return g_string_free(bar, FALSE);
1153 const GScannerConfig scan_config = {
1156 ) /* cset_skip_characters */,
1161 ) /* cset_identifier_first */,
1168 ) /* cset_identifier_nth */,
1169 ( "" ) /* cpair_comment_single */,
1171 TRUE /* case_sensitive */,
1173 FALSE /* skip_comment_multi */,
1174 FALSE /* skip_comment_single */,
1175 FALSE /* scan_comment_multi */,
1176 TRUE /* scan_identifier */,
1177 TRUE /* scan_identifier_1char */,
1178 FALSE /* scan_identifier_NULL */,
1179 TRUE /* scan_symbols */,
1180 FALSE /* scan_binary */,
1181 FALSE /* scan_octal */,
1182 FALSE /* scan_float */,
1183 FALSE /* scan_hex */,
1184 FALSE /* scan_hex_dollar */,
1185 FALSE /* scan_string_sq */,
1186 FALSE /* scan_string_dq */,
1187 TRUE /* numbers_2_int */,
1188 FALSE /* int_2_float */,
1189 FALSE /* identifier_2_string */,
1190 FALSE /* char_2_token */,
1191 FALSE /* symbol_2_token */,
1192 TRUE /* scope_0_fallback */,
1197 uzbl.scan = g_scanner_new(&scan_config);
1198 while(symp->symbol_name) {
1199 g_scanner_scope_add_symbol(uzbl.scan, 0,
1201 GINT_TO_POINTER(symp->symbol_token));
1207 expand_template(const char *template, gboolean escape_markup) {
1208 if(!template) return NULL;
1210 GTokenType token = G_TOKEN_NONE;
1211 GString *ret = g_string_new("");
1215 g_scanner_input_text(uzbl.scan, template, strlen(template));
1216 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1217 token = g_scanner_get_next_token(uzbl.scan);
1219 if(token == G_TOKEN_SYMBOL) {
1220 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1224 buf = uzbl.state.uri ?
1225 g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1226 g_string_append(ret, buf);
1230 g_string_append(ret, uzbl.state.uri ?
1231 uzbl.state.uri : g_strdup(""));
1234 buf = itos(uzbl.gui.sbar.load_progress);
1235 g_string_append(ret, buf);
1238 case SYM_LOADPRGSBAR:
1239 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1240 g_string_append(ret, buf);
1245 buf = uzbl.gui.main_title ?
1246 g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1247 g_string_append(ret, buf);
1251 g_string_append(ret, uzbl.gui.main_title ?
1252 uzbl.gui.main_title : "");
1254 case SYM_SELECTED_URI:
1256 buf = uzbl.state.selected_url ?
1257 g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1258 g_string_append(ret, buf);
1262 g_string_append(ret, uzbl.state.selected_url ?
1263 uzbl.state.selected_url : "");
1266 buf = itos(uzbl.xwin);
1267 g_string_append(ret,
1268 uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1273 buf = uzbl.state.keycmd->str ?
1274 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1275 g_string_append(ret, buf);
1279 g_string_append(ret, uzbl.state.keycmd->str ?
1280 uzbl.state.keycmd->str : "");
1283 g_string_append(ret,
1284 uzbl.behave.insert_mode ?
1285 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1288 g_string_append(ret,
1289 uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1291 /* useragent syms */
1293 buf = itos(WEBKIT_MAJOR_VERSION);
1294 g_string_append(ret, buf);
1298 buf = itos(WEBKIT_MINOR_VERSION);
1299 g_string_append(ret, buf);
1303 buf = itos(WEBKIT_MICRO_VERSION);
1304 g_string_append(ret, buf);
1308 g_string_append(ret, uzbl.state.unameinfo.sysname);
1311 g_string_append(ret, uzbl.state.unameinfo.nodename);
1314 g_string_append(ret, uzbl.state.unameinfo.release);
1317 g_string_append(ret, uzbl.state.unameinfo.version);
1320 g_string_append(ret, uzbl.state.unameinfo.machine);
1323 g_string_append(ret, ARCH);
1326 case SYM_DOMAINNAME:
1327 g_string_append(ret, uzbl.state.unameinfo.domainname);
1331 g_string_append(ret, COMMIT);
1337 else if(token == G_TOKEN_INT) {
1338 g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1340 else if(token == G_TOKEN_IDENTIFIER) {
1341 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1343 else if(token == G_TOKEN_CHAR) {
1344 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1346 else if(token == G_TOKEN_ERROR) {
1347 g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.",
1349 g_scanner_cur_line(uzbl.scan),
1350 g_scanner_cur_position(uzbl.scan));
1354 return g_string_free(ret, FALSE);
1356 /* --End Statusbar functions-- */
1359 sharg_append(GArray *a, const gchar *str) {
1360 const gchar *s = (str ? str : "");
1361 g_array_append_val(a, s);
1364 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1366 run_command (const gchar *command, const guint npre, const gchar **args,
1367 const gboolean sync, char **output_stdout) {
1368 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1371 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1372 gchar *pid = itos(getpid());
1373 gchar *xwin = itos(uzbl.xwin);
1375 sharg_append(a, command);
1376 for (i = 0; i < npre; i++) /* add n args before the default vars */
1377 sharg_append(a, args[i]);
1378 sharg_append(a, uzbl.state.config_file);
1379 sharg_append(a, pid);
1380 sharg_append(a, xwin);
1381 sharg_append(a, uzbl.comm.fifo_path);
1382 sharg_append(a, uzbl.comm.socket_path);
1383 sharg_append(a, uzbl.state.uri);
1384 sharg_append(a, uzbl.gui.main_title);
1386 for (i = npre; i < g_strv_length((gchar**)args); i++)
1387 sharg_append(a, args[i]);
1391 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1393 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1394 NULL, NULL, output_stdout, NULL, NULL, &err);
1395 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1396 NULL, NULL, NULL, &err);
1398 if (uzbl.state.verbose) {
1399 GString *s = g_string_new("spawned:");
1400 for (i = 0; i < (a->len); i++) {
1401 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1402 g_string_append_printf(s, " %s", qarg);
1405 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1406 printf("%s\n", s->str);
1407 g_string_free(s, TRUE);
1409 printf("Stdout: %s\n", *output_stdout);
1413 g_printerr("error on run_command: %s\n", err->message);
1418 g_array_free (a, TRUE);
1423 split_quoted(const gchar* src, const gboolean unquote) {
1424 /* split on unquoted space, return array of strings;
1425 remove a layer of quotes and backslashes if unquote */
1426 if (!src) return NULL;
1428 gboolean dq = FALSE;
1429 gboolean sq = FALSE;
1430 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1431 GString *s = g_string_new ("");
1435 for (p = src; *p != '\0'; p++) {
1436 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1437 else if (*p == '\\') { g_string_append_c(s, *p++);
1438 g_string_append_c(s, *p); }
1439 else if ((*p == '"') && unquote && !sq) dq = !dq;
1440 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1442 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1443 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1445 else if ((*p == ' ') && !dq && !sq) {
1446 dup = g_strdup(s->str);
1447 g_array_append_val(a, dup);
1448 g_string_truncate(s, 0);
1449 } else g_string_append_c(s, *p);
1451 dup = g_strdup(s->str);
1452 g_array_append_val(a, dup);
1453 ret = (gchar**)a->data;
1454 g_array_free (a, FALSE);
1455 g_string_free (s, TRUE);
1460 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1461 (void)web_view; (void)result;
1462 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1463 if (argv_idx(argv, 0))
1464 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1468 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1469 (void)web_view; (void)result;
1471 if (argv_idx(argv, 0))
1472 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1473 TRUE, &uzbl.comm.sync_stdout);
1477 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1478 (void)web_view; (void)result;
1479 if (!uzbl.behave.shell_cmd) {
1480 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1485 gchar *spacer = g_strdup("");
1486 g_array_insert_val(argv, 1, spacer);
1487 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1489 for (i = 1; i < g_strv_length(cmd); i++)
1490 g_array_prepend_val(argv, cmd[i]);
1492 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1498 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1499 (void)web_view; (void)result;
1500 if (!uzbl.behave.shell_cmd) {
1501 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1506 gchar *spacer = g_strdup("");
1507 g_array_insert_val(argv, 1, spacer);
1508 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1510 for (i = 1; i < g_strv_length(cmd); i++)
1511 g_array_prepend_val(argv, cmd[i]);
1513 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1514 TRUE, &uzbl.comm.sync_stdout);
1520 parse_command(const char *cmd, const char *param, GString *result) {
1523 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1525 gchar **par = split_quoted(param, TRUE);
1526 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1528 if (c->no_split) { /* don't split */
1529 sharg_append(a, param);
1531 for (i = 0; i < g_strv_length(par); i++)
1532 sharg_append(a, par[i]);
1535 if (result == NULL) {
1536 GString *result_print = g_string_new("");
1538 c->function(uzbl.gui.web_view, a, result_print);
1539 if (result_print->len)
1540 printf("%*s\n", result_print->len, result_print->str);
1542 g_string_free(result_print, TRUE);
1544 c->function(uzbl.gui.web_view, a, result);
1547 g_array_free (a, TRUE);
1550 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1557 if(*uzbl.net.proxy_url == ' '
1558 || uzbl.net.proxy_url == NULL) {
1559 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1560 (GType) SOUP_SESSION_PROXY_URI);
1563 suri = soup_uri_new(uzbl.net.proxy_url);
1564 g_object_set(G_OBJECT(uzbl.net.soup_session),
1565 SOUP_SESSION_PROXY_URI,
1567 soup_uri_free(suri);
1574 if(file_exists(uzbl.gui.icon)) {
1575 if (uzbl.gui.main_window)
1576 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1578 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1584 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1585 g_array_append_val (a, uzbl.state.uri);
1586 load_uri(uzbl.gui.web_view, a, NULL);
1587 g_array_free (a, TRUE);
1591 cmd_always_insert_mode() {
1592 uzbl.behave.insert_mode =
1593 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1599 g_object_set(G_OBJECT(uzbl.net.soup_session),
1600 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1604 cmd_max_conns_host() {
1605 g_object_set(G_OBJECT(uzbl.net.soup_session),
1606 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1611 soup_session_remove_feature
1612 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1613 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1614 /*g_free(uzbl.net.soup_logger);*/
1616 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1617 soup_session_add_feature(uzbl.net.soup_session,
1618 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1623 return webkit_web_view_get_settings(uzbl.gui.web_view);
1628 WebKitWebSettings *ws = view_settings();
1629 if (uzbl.behave.font_size > 0) {
1630 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1633 if (uzbl.behave.monospace_size > 0) {
1634 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1635 uzbl.behave.monospace_size, NULL);
1637 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1638 uzbl.behave.font_size, NULL);
1644 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1648 cmd_disable_plugins() {
1649 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1650 !uzbl.behave.disable_plugins, NULL);
1654 cmd_disable_scripts() {
1655 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1656 !uzbl.behave.disable_scripts, NULL);
1660 cmd_minimum_font_size() {
1661 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1662 uzbl.behave.minimum_font_size, NULL);
1665 cmd_autoload_img() {
1666 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1667 uzbl.behave.autoload_img, NULL);
1672 cmd_autoshrink_img() {
1673 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1674 uzbl.behave.autoshrink_img, NULL);
1679 cmd_enable_spellcheck() {
1680 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1681 uzbl.behave.enable_spellcheck, NULL);
1685 cmd_enable_private() {
1686 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1687 uzbl.behave.enable_private, NULL);
1692 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1693 uzbl.behave.print_bg, NULL);
1698 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1699 uzbl.behave.style_uri, NULL);
1703 cmd_resizable_txt() {
1704 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1705 uzbl.behave.resizable_txt, NULL);
1709 cmd_default_encoding() {
1710 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1711 uzbl.behave.default_encoding, NULL);
1715 cmd_enforce_96dpi() {
1716 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1717 uzbl.behave.enforce_96dpi, NULL);
1721 cmd_caret_browsing() {
1722 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1723 uzbl.behave.caret_browsing, NULL);
1727 cmd_cookie_handler() {
1728 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1729 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1730 if ((g_strcmp0(split[0], "sh") == 0) ||
1731 (g_strcmp0(split[0], "spawn") == 0)) {
1732 g_free (uzbl.behave.cookie_handler);
1733 uzbl.behave.cookie_handler =
1734 g_strdup_printf("sync_%s %s", split[0], split[1]);
1741 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1742 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1743 if ((g_strcmp0(split[0], "sh") == 0) ||
1744 (g_strcmp0(split[0], "spawn") == 0)) {
1745 g_free (uzbl.behave.new_window);
1746 uzbl.behave.new_window =
1747 g_strdup_printf("%s %s", split[0], split[1]);
1754 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1759 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1764 if(uzbl.behave.inject_html) {
1765 webkit_web_view_load_html_string (uzbl.gui.web_view,
1766 uzbl.behave.inject_html, NULL);
1775 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1776 uzbl.behave.modmask = 0;
1778 if(uzbl.behave.modkey)
1779 g_free(uzbl.behave.modkey);
1780 uzbl.behave.modkey = buf;
1782 for (i = 0; modkeys[i].key != NULL; i++) {
1783 if (g_strrstr(buf, modkeys[i].key))
1784 uzbl.behave.modmask |= modkeys[i].mask;
1790 if (*uzbl.net.useragent == ' ') {
1791 g_free (uzbl.net.useragent);
1792 uzbl.net.useragent = NULL;
1794 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1796 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1797 g_free(uzbl.net.useragent);
1798 uzbl.net.useragent = ua;
1804 gtk_widget_ref(uzbl.gui.scrolled_win);
1805 gtk_widget_ref(uzbl.gui.mainbar);
1806 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1807 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1809 if(uzbl.behave.status_top) {
1810 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1811 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1814 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.mainbar, FALSE, TRUE, 0);
1817 gtk_widget_unref(uzbl.gui.scrolled_win);
1818 gtk_widget_unref(uzbl.gui.mainbar);
1819 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1824 set_var_value(gchar *name, gchar *val) {
1825 uzbl_cmdprop *c = NULL;
1829 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1830 /* check for the variable type */
1831 if (c->type == TYPE_STR) {
1832 buf = expand(val, 0);
1835 } else if(c->type == TYPE_INT) {
1836 int *ip = (int *)c->ptr;
1837 buf = expand(val, 0);
1838 *ip = (int)strtoul(buf, &endp, 10);
1840 } else if (c->type == TYPE_FLOAT) {
1841 float *fp = (float *)c->ptr;
1842 buf = expand(val, 0);
1843 *fp = strtod(buf, &endp);
1847 /* invoke a command specific function */
1848 if(c->func) c->func();
1855 Behaviour *b = &uzbl.behave;
1857 if(b->html_buffer->str) {
1858 webkit_web_view_load_html_string (uzbl.gui.web_view,
1859 b->html_buffer->str, b->base_url);
1860 g_string_free(b->html_buffer, TRUE);
1861 b->html_buffer = g_string_new("");
1865 enum {M_CMD, M_HTML};
1867 parse_cmd_line(const char *ctl_line, GString *result) {
1868 Behaviour *b = &uzbl.behave;
1871 if(b->mode == M_HTML) {
1872 len = strlen(b->html_endmarker);
1873 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1874 if(len == strlen(ctl_line)-1 &&
1875 !strncmp(b->html_endmarker, ctl_line, len)) {
1877 set_var_value("mode", "0");
1882 set_timeout(b->html_timeout);
1883 g_string_append(b->html_buffer, ctl_line);
1886 else if((ctl_line[0] == '#') /* Comments */
1887 || (ctl_line[0] == ' ')
1888 || (ctl_line[0] == '\n'))
1889 ; /* ignore these lines */
1890 else { /* parse a command */
1892 gchar **tokens = NULL;
1893 len = strlen(ctl_line);
1895 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1896 ctlstrip = g_strndup(ctl_line, len - 1);
1897 else ctlstrip = g_strdup(ctl_line);
1899 tokens = g_strsplit(ctlstrip, " ", 2);
1900 parse_command(tokens[0], tokens[1], result);
1907 build_stream_name(int type, const gchar* dir) {
1908 char *xwin_str = NULL;
1909 State *s = &uzbl.state;
1912 xwin_str = itos((int)uzbl.xwin);
1914 str = g_strdup_printf
1915 ("%s/uzbl_fifo_%s", dir,
1916 s->instance_name ? s->instance_name : xwin_str);
1917 } else if (type == SOCKET) {
1918 str = g_strdup_printf
1919 ("%s/uzbl_socket_%s", dir,
1920 s->instance_name ? s->instance_name : xwin_str );
1927 control_fifo(GIOChannel *gio, GIOCondition condition) {
1928 if (uzbl.state.verbose)
1929 printf("triggered\n");
1934 if (condition & G_IO_HUP)
1935 g_error ("Fifo: Read end of pipe died!\n");
1938 g_error ("Fifo: GIOChannel broke\n");
1940 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1941 if (ret == G_IO_STATUS_ERROR) {
1942 g_error ("Fifo: Error reading: %s\n", err->message);
1946 parse_cmd_line(ctl_line, NULL);
1953 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1954 GIOChannel *chan = NULL;
1955 GError *error = NULL;
1956 gchar *path = build_stream_name(FIFO, dir);
1958 if (!file_exists(path)) {
1959 if (mkfifo (path, 0666) == 0) {
1960 // 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.
1961 chan = g_io_channel_new_file(path, "r+", &error);
1963 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1964 if (uzbl.state.verbose)
1965 printf ("init_fifo: created successfully as %s\n", path);
1966 uzbl.comm.fifo_path = path;
1968 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1969 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1970 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1971 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1973 /* if we got this far, there was an error; cleanup */
1974 if (error) g_error_free (error);
1981 control_stdin(GIOChannel *gio, GIOCondition condition) {
1983 gchar *ctl_line = NULL;
1986 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1987 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1990 parse_cmd_line(ctl_line, NULL);
1998 GIOChannel *chan = NULL;
1999 GError *error = NULL;
2001 chan = g_io_channel_unix_new(fileno(stdin));
2003 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2004 g_error ("Stdin: could not add watch\n");
2006 if (uzbl.state.verbose)
2007 printf ("Stdin: watch added successfully\n");
2010 g_error ("Stdin: Error while opening: %s\n", error->message);
2012 if (error) g_error_free (error);
2016 control_socket(GIOChannel *chan) {
2017 struct sockaddr_un remote;
2018 unsigned int t = sizeof(remote);
2020 GIOChannel *clientchan;
2022 clientsock = accept (g_io_channel_unix_get_fd(chan),
2023 (struct sockaddr *) &remote, &t);
2025 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2026 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2027 (GIOFunc) control_client_socket, clientchan);
2034 control_client_socket(GIOChannel *clientchan) {
2036 GString *result = g_string_new("");
2037 GError *error = NULL;
2041 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2042 if (ret == G_IO_STATUS_ERROR) {
2043 g_warning ("Error reading: %s\n", error->message);
2044 g_io_channel_shutdown(clientchan, TRUE, &error);
2046 } else if (ret == G_IO_STATUS_EOF) {
2047 /* shutdown and remove channel watch from main loop */
2048 g_io_channel_shutdown(clientchan, TRUE, &error);
2053 parse_cmd_line (ctl_line, result);
2054 g_string_append_c(result, '\n');
2055 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2057 if (ret == G_IO_STATUS_ERROR) {
2058 g_warning ("Error writing: %s", error->message);
2060 g_io_channel_flush(clientchan, &error);
2063 if (error) g_error_free (error);
2064 g_string_free(result, TRUE);
2070 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2071 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2072 if (unlink(uzbl.comm.socket_path) == -1)
2073 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2074 g_free(uzbl.comm.socket_path);
2075 uzbl.comm.socket_path = NULL;
2083 GIOChannel *chan = NULL;
2085 struct sockaddr_un local;
2086 gchar *path = build_stream_name(SOCKET, dir);
2088 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2090 local.sun_family = AF_UNIX;
2091 strcpy (local.sun_path, path);
2092 unlink (local.sun_path);
2094 len = strlen (local.sun_path) + sizeof (local.sun_family);
2095 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2096 if (uzbl.state.verbose)
2097 printf ("init_socket: opened in %s\n", path);
2100 if( (chan = g_io_channel_unix_new(sock)) ) {
2101 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2102 uzbl.comm.socket_path = path;
2105 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2107 /* if we got this far, there was an error; cleanup */
2114 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2115 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2117 // this function may be called very early when the templates are not set (yet), hence the checks
2119 update_title (void) {
2120 Behaviour *b = &uzbl.behave;
2123 if (b->show_status) {
2124 if (b->title_format_short) {
2125 parsed = expand_template(b->title_format_short, FALSE);
2126 if (uzbl.gui.main_window)
2127 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2130 if (b->status_format) {
2131 parsed = expand_template(b->status_format, TRUE);
2132 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2135 if (b->status_background) {
2137 gdk_color_parse (b->status_background, &color);
2138 //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)
2139 if (uzbl.gui.main_window)
2140 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2141 else if (uzbl.gui.plug)
2142 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2145 if (b->title_format_long) {
2146 parsed = expand_template(b->title_format_long, FALSE);
2147 if (uzbl.gui.main_window)
2148 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2155 key_press_cb (GtkWidget* window, GdkEventKey* event)
2157 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2161 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2162 || 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)
2165 /* turn off insert mode (if always_insert_mode is not used) */
2166 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2167 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2172 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2175 if (event->keyval == GDK_Escape) {
2176 g_string_truncate(uzbl.state.keycmd, 0);
2178 dehilight(uzbl.gui.web_view, NULL, NULL);
2182 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2183 if (event->keyval == GDK_Insert) {
2185 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2186 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2188 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2191 g_string_append (uzbl.state.keycmd, str);
2198 if (event->keyval == GDK_BackSpace)
2199 keycmd_bs(NULL, NULL, NULL);
2201 gboolean key_ret = FALSE;
2202 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2204 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2206 run_keycmd(key_ret);
2208 if (key_ret) return (!uzbl.behave.insert_mode);
2213 run_keycmd(const gboolean key_ret) {
2214 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2216 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2217 g_string_truncate(uzbl.state.keycmd, 0);
2218 parse_command(act->name, act->param, NULL);
2222 /* try if it's an incremental keycmd or one that takes args, and run it */
2223 GString* short_keys = g_string_new ("");
2224 GString* short_keys_inc = g_string_new ("");
2226 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2227 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2228 g_string_assign(short_keys_inc, short_keys->str);
2229 g_string_append_c(short_keys, '_');
2230 g_string_append_c(short_keys_inc, '*');
2232 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2233 /* run normal cmds only if return was pressed */
2234 exec_paramcmd(act, i);
2235 g_string_truncate(uzbl.state.keycmd, 0);
2237 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2238 if (key_ret) /* just quit the incremental command on return */
2239 g_string_truncate(uzbl.state.keycmd, 0);
2240 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2244 g_string_truncate(short_keys, short_keys->len - 1);
2246 g_string_free (short_keys, TRUE);
2247 g_string_free (short_keys_inc, TRUE);
2251 exec_paramcmd(const Action *act, const guint i) {
2252 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2253 GString *actionname = g_string_new ("");
2254 GString *actionparam = g_string_new ("");
2255 g_string_erase (parampart, 0, i+1);
2257 g_string_printf (actionname, act->name, parampart->str);
2259 g_string_printf (actionparam, act->param, parampart->str);
2260 parse_command(actionname->str, actionparam->str, NULL);
2261 g_string_free(actionname, TRUE);
2262 g_string_free(actionparam, TRUE);
2263 g_string_free(parampart, TRUE);
2271 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2272 //main_window_ref = g_object_ref(scrolled_window);
2273 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
2275 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2276 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2278 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2279 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2280 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2281 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2282 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2283 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2284 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2285 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2286 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2287 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2288 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2290 return scrolled_window;
2297 g->mainbar = gtk_hbox_new (FALSE, 0);
2299 /* keep a reference to the bar so we can re-pack it at runtime*/
2300 //sbar_ref = g_object_ref(g->mainbar);
2302 g->mainbar_label = gtk_label_new ("");
2303 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2304 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2305 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2306 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2307 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2308 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2314 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2315 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2316 gtk_widget_set_name (window, "Uzbl browser");
2317 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2318 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2325 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2326 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2327 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2334 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2336 If actname is one that calls an external command, this function will inject
2337 newargs in front of the user-provided args in that command line. They will
2338 come become after the body of the script (in sh) or after the name of
2339 the command to execute (in spawn).
2340 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2341 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2343 The return value consist of two strings: the action (sh, ...) and its args.
2345 If act is not one that calls an external command, then the given action merely
2348 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2349 /* Arrr! Here be memory leaks */
2350 gchar *actdup = g_strdup(actname);
2351 g_array_append_val(rets, actdup);
2353 if ((g_strcmp0(actname, "spawn") == 0) ||
2354 (g_strcmp0(actname, "sh") == 0) ||
2355 (g_strcmp0(actname, "sync_spawn") == 0) ||
2356 (g_strcmp0(actname, "sync_sh") == 0)) {
2358 GString *a = g_string_new("");
2359 gchar **spawnparts = split_quoted(origargs, FALSE);
2360 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2361 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2363 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2364 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2366 g_array_append_val(rets, a->str);
2367 g_string_free(a, FALSE);
2368 g_strfreev(spawnparts);
2370 gchar *origdup = g_strdup(origargs);
2371 g_array_append_val(rets, origdup);
2373 return (gchar**)g_array_free(rets, FALSE);
2377 run_handler (const gchar *act, const gchar *args) {
2378 /* Consider this code a temporary hack to make the handlers usable.
2379 In practice, all this splicing, injection, and reconstruction is
2380 inefficient, annoying and hard to manage. Potential pitfalls arise
2381 when the handler specific args 1) are not quoted (the handler
2382 callbacks should take care of this) 2) are quoted but interfere
2383 with the users' own quotation. A more ideal solution is
2384 to refactor parse_command so that it doesn't just take a string
2385 and execute it; rather than that, we should have a function which
2386 returns the argument vector parsed from the string. This vector
2387 could be modified (e.g. insert additional args into it) before
2388 passing it to the next function that actually executes it. Though
2389 it still isn't perfect for chain actions.. will reconsider & re-
2390 factor when I have the time. -duc */
2392 char **parts = g_strsplit(act, " ", 2);
2394 if (g_strcmp0(parts[0], "chain") == 0) {
2395 GString *newargs = g_string_new("");
2396 gchar **chainparts = split_quoted(parts[1], FALSE);
2398 /* for every argument in the chain, inject the handler args
2399 and make sure the new parts are wrapped in quotes */
2400 gchar **cp = chainparts;
2402 gchar *quotless = NULL;
2403 gchar **spliced_quotless = NULL; // sigh -_-;
2404 gchar **inpart = NULL;
2407 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2409 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2410 } else quotless = g_strdup(*cp);
2412 spliced_quotless = g_strsplit(quotless, " ", 2);
2413 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2414 g_strfreev(spliced_quotless);
2416 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2422 parse_command(parts[0], &(newargs->str[1]), NULL);
2423 g_string_free(newargs, TRUE);
2424 g_strfreev(chainparts);
2427 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2428 parse_command(inparts[0], inparts[1], NULL);
2436 add_binding (const gchar *key, const gchar *act) {
2437 char **parts = g_strsplit(act, " ", 2);
2444 if (uzbl.state.verbose)
2445 printf ("Binding %-10s : %s\n", key, act);
2446 action = new_action(parts[0], parts[1]);
2448 if (g_hash_table_remove (uzbl.bindings, key))
2449 g_warning ("Overwriting existing binding for \"%s\"", key);
2450 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2455 get_xdg_var (XDG_Var xdg) {
2456 const gchar* actual_value = getenv (xdg.environmental);
2457 const gchar* home = getenv ("HOME");
2458 gchar* return_value;
2460 if (! actual_value || strcmp (actual_value, "") == 0) {
2461 if (xdg.default_value) {
2462 return_value = str_replace ("~", home, xdg.default_value);
2464 return_value = NULL;
2467 return_value = str_replace("~", home, actual_value);
2470 return return_value;
2474 find_xdg_file (int xdg_type, char* filename) {
2475 /* xdg_type = 0 => config
2476 xdg_type = 1 => data
2477 xdg_type = 2 => cache*/
2479 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2480 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2483 gchar* temporary_string;
2487 if (! file_exists (temporary_file) && xdg_type != 2) {
2488 buf = get_xdg_var (XDG[3 + xdg_type]);
2489 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2492 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2493 g_free (temporary_file);
2494 temporary_file = g_strconcat (temporary_string, filename, NULL);
2498 //g_free (temporary_string); - segfaults.
2500 if (file_exists (temporary_file)) {
2501 return temporary_file;
2508 State *s = &uzbl.state;
2509 Network *n = &uzbl.net;
2511 for (i = 0; default_config[i].command != NULL; i++) {
2512 parse_cmd_line(default_config[i].command, NULL);
2515 if (g_strcmp0(s->config_file, "-") == 0) {
2516 s->config_file = NULL;
2520 else if (!s->config_file) {
2521 s->config_file = find_xdg_file (0, "/uzbl/config");
2524 if (s->config_file) {
2525 GArray* lines = read_file_by_line (s->config_file);
2529 while ((line = g_array_index(lines, gchar*, i))) {
2530 parse_cmd_line (line, NULL);
2534 g_array_free (lines, TRUE);
2536 if (uzbl.state.verbose)
2537 printf ("No configuration file loaded.\n");
2540 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2543 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2546 if (!uzbl.behave.cookie_handler)
2549 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2550 GString *s = g_string_new ("");
2551 SoupURI * soup_uri = soup_message_get_uri(msg);
2552 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2553 run_handler(uzbl.behave.cookie_handler, s->str);
2555 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2556 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2557 if ( p != NULL ) *p = '\0';
2558 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2560 if (uzbl.comm.sync_stdout)
2561 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2563 g_string_free(s, TRUE);
2567 save_cookies (SoupMessage *msg, gpointer user_data){
2571 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2572 cookie = soup_cookie_to_set_cookie_header(ck->data);
2573 SoupURI * soup_uri = soup_message_get_uri(msg);
2574 GString *s = g_string_new ("");
2575 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2576 run_handler(uzbl.behave.cookie_handler, s->str);
2578 g_string_free(s, TRUE);
2583 /* --- WEBINSPECTOR --- */
2585 hide_window_cb(GtkWidget *widget, gpointer data) {
2588 gtk_widget_hide(widget);
2592 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2595 (void) web_inspector;
2596 GtkWidget* scrolled_window;
2597 GtkWidget* new_web_view;
2600 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2601 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2602 G_CALLBACK(hide_window_cb), NULL);
2604 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2605 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2606 gtk_widget_show(g->inspector_window);
2608 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2609 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2610 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2611 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2612 gtk_widget_show(scrolled_window);
2614 new_web_view = webkit_web_view_new();
2615 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2617 return WEBKIT_WEB_VIEW(new_web_view);
2621 inspector_show_window_cb (WebKitWebInspector* inspector){
2623 gtk_widget_show(uzbl.gui.inspector_window);
2627 /* TODO: Add variables and code to make use of these functions */
2629 inspector_close_window_cb (WebKitWebInspector* inspector){
2635 inspector_attach_window_cb (WebKitWebInspector* inspector){
2641 inspector_detach_window_cb (WebKitWebInspector* inspector){
2647 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2653 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2659 set_up_inspector() {
2661 WebKitWebSettings *settings = view_settings();
2662 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2664 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2665 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2666 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2667 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2668 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2669 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2670 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2672 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2676 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2678 uzbl_cmdprop *c = v;
2683 if(c->type == TYPE_STR)
2684 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2685 else if(c->type == TYPE_INT)
2686 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2687 else if(c->type == TYPE_FLOAT)
2688 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2692 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2696 printf("bind %s = %s %s\n", (char *)k ,
2697 (char *)a->name, a->param?(char *)a->param:"");
2702 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2703 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2706 #ifndef UZBL_LIBRARY
2709 main (int argc, char* argv[]) {
2710 gtk_init (&argc, &argv);
2711 if (!g_thread_supported ())
2712 g_thread_init (NULL);
2713 uzbl.state.executable_path = g_strdup(argv[0]);
2714 uzbl.state.selected_url = NULL;
2715 uzbl.state.searchtx = NULL;
2717 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2718 g_option_context_add_main_entries (context, entries, NULL);
2719 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2720 g_option_context_parse (context, &argc, &argv, NULL);
2721 g_option_context_free(context);
2723 if (uzbl.behave.print_version) {
2724 printf("Commit: %s\n", COMMIT);
2728 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2729 if (argc > 1 && !uzbl.state.uri)
2730 uri_override = g_strdup(argv[1]);
2731 gboolean verbose_override = uzbl.state.verbose;
2733 /* initialize hash table */
2734 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2736 uzbl.net.soup_session = webkit_get_default_session();
2737 uzbl.state.keycmd = g_string_new("");
2739 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2740 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2741 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2742 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2743 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2744 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2747 if(uname(&uzbl.state.unameinfo) == -1)
2748 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2750 uzbl.gui.sbar.progress_s = g_strdup("=");
2751 uzbl.gui.sbar.progress_u = g_strdup("·");
2752 uzbl.gui.sbar.progress_w = 10;
2754 /* HTML mode defaults*/
2755 uzbl.behave.html_buffer = g_string_new("");
2756 uzbl.behave.html_endmarker = g_strdup(".");
2757 uzbl.behave.html_timeout = 60;
2758 uzbl.behave.base_url = g_strdup("http://invalid");
2760 /* default mode indicators */
2761 uzbl.behave.insert_indicator = g_strdup("I");
2762 uzbl.behave.cmd_indicator = g_strdup("C");
2766 make_var_to_name_hash();
2768 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2770 uzbl.gui.scrolled_win = create_browser();
2773 /* initial packing */
2774 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2775 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2777 if (uzbl.state.socket_id) {
2778 uzbl.gui.plug = create_plug ();
2779 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2780 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2782 uzbl.gui.main_window = create_window ();
2783 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2784 gtk_widget_show_all (uzbl.gui.main_window);
2785 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2788 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2790 if (uzbl.state.verbose) {
2791 printf("Uzbl start location: %s\n", argv[0]);
2792 if (uzbl.state.socket_id)
2793 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2795 printf("window_id %i\n",(int) uzbl.xwin);
2796 printf("pid %i\n", getpid ());
2797 printf("name: %s\n", uzbl.state.instance_name);
2800 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2801 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2802 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2803 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2804 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2808 if (!uzbl.behave.show_status)
2809 gtk_widget_hide(uzbl.gui.mainbar);
2816 if (verbose_override > uzbl.state.verbose)
2817 uzbl.state.verbose = verbose_override;
2820 set_var_value("uri", uri_override);
2821 g_free(uri_override);
2822 } else if (uzbl.state.uri)
2823 cmd_load_uri(uzbl.gui.web_view, NULL);
2828 return EXIT_SUCCESS;
2832 /* vi: set et ts=4: */