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 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1955 if (unlink(uzbl.comm.fifo_path) == -1)
1956 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1957 g_free(uzbl.comm.fifo_path);
1958 uzbl.comm.fifo_path = NULL;
1961 GIOChannel *chan = NULL;
1962 GError *error = NULL;
1963 gchar *path = build_stream_name(FIFO, dir);
1965 if (!file_exists(path)) {
1966 if (mkfifo (path, 0666) == 0) {
1967 // 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.
1968 chan = g_io_channel_new_file(path, "r+", &error);
1970 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1971 if (uzbl.state.verbose)
1972 printf ("init_fifo: created successfully as %s\n", path);
1973 uzbl.comm.fifo_path = path;
1975 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1976 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1977 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1978 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1980 /* if we got this far, there was an error; cleanup */
1981 if (error) g_error_free (error);
1988 control_stdin(GIOChannel *gio, GIOCondition condition) {
1990 gchar *ctl_line = NULL;
1993 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1994 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1997 parse_cmd_line(ctl_line, NULL);
2005 GIOChannel *chan = NULL;
2006 GError *error = NULL;
2008 chan = g_io_channel_unix_new(fileno(stdin));
2010 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2011 g_error ("Stdin: could not add watch\n");
2013 if (uzbl.state.verbose)
2014 printf ("Stdin: watch added successfully\n");
2017 g_error ("Stdin: Error while opening: %s\n", error->message);
2019 if (error) g_error_free (error);
2023 control_socket(GIOChannel *chan) {
2024 struct sockaddr_un remote;
2025 unsigned int t = sizeof(remote);
2027 GIOChannel *clientchan;
2029 clientsock = accept (g_io_channel_unix_get_fd(chan),
2030 (struct sockaddr *) &remote, &t);
2032 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2033 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2034 (GIOFunc) control_client_socket, clientchan);
2041 control_client_socket(GIOChannel *clientchan) {
2043 GString *result = g_string_new("");
2044 GError *error = NULL;
2048 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2049 if (ret == G_IO_STATUS_ERROR) {
2050 g_warning ("Error reading: %s\n", error->message);
2051 g_io_channel_shutdown(clientchan, TRUE, &error);
2053 } else if (ret == G_IO_STATUS_EOF) {
2054 /* shutdown and remove channel watch from main loop */
2055 g_io_channel_shutdown(clientchan, TRUE, &error);
2060 parse_cmd_line (ctl_line, result);
2061 g_string_append_c(result, '\n');
2062 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2064 if (ret == G_IO_STATUS_ERROR) {
2065 g_warning ("Error writing: %s", error->message);
2067 g_io_channel_flush(clientchan, &error);
2070 if (error) g_error_free (error);
2071 g_string_free(result, TRUE);
2077 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2078 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2079 if (unlink(uzbl.comm.socket_path) == -1)
2080 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2081 g_free(uzbl.comm.socket_path);
2082 uzbl.comm.socket_path = NULL;
2090 GIOChannel *chan = NULL;
2092 struct sockaddr_un local;
2093 gchar *path = build_stream_name(SOCKET, dir);
2095 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2097 local.sun_family = AF_UNIX;
2098 strcpy (local.sun_path, path);
2099 unlink (local.sun_path);
2101 len = strlen (local.sun_path) + sizeof (local.sun_family);
2102 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2103 if (uzbl.state.verbose)
2104 printf ("init_socket: opened in %s\n", path);
2107 if( (chan = g_io_channel_unix_new(sock)) ) {
2108 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2109 uzbl.comm.socket_path = path;
2112 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2114 /* if we got this far, there was an error; cleanup */
2121 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2122 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2124 // this function may be called very early when the templates are not set (yet), hence the checks
2126 update_title (void) {
2127 Behaviour *b = &uzbl.behave;
2130 if (b->show_status) {
2131 if (b->title_format_short) {
2132 parsed = expand_template(b->title_format_short, FALSE);
2133 if (uzbl.gui.main_window)
2134 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2137 if (b->status_format) {
2138 parsed = expand_template(b->status_format, TRUE);
2139 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2142 if (b->status_background) {
2144 gdk_color_parse (b->status_background, &color);
2145 //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)
2146 if (uzbl.gui.main_window)
2147 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2148 else if (uzbl.gui.plug)
2149 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2152 if (b->title_format_long) {
2153 parsed = expand_template(b->title_format_long, FALSE);
2154 if (uzbl.gui.main_window)
2155 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2162 key_press_cb (GtkWidget* window, GdkEventKey* event)
2164 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2168 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2169 || 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)
2172 /* turn off insert mode (if always_insert_mode is not used) */
2173 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2174 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2179 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2182 if (event->keyval == GDK_Escape) {
2183 g_string_truncate(uzbl.state.keycmd, 0);
2185 dehilight(uzbl.gui.web_view, NULL, NULL);
2189 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2190 if (event->keyval == GDK_Insert) {
2192 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2193 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2195 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2198 g_string_append (uzbl.state.keycmd, str);
2205 if (event->keyval == GDK_BackSpace)
2206 keycmd_bs(NULL, NULL, NULL);
2208 gboolean key_ret = FALSE;
2209 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2211 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2213 run_keycmd(key_ret);
2215 if (key_ret) return (!uzbl.behave.insert_mode);
2220 run_keycmd(const gboolean key_ret) {
2221 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2223 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2224 g_string_truncate(uzbl.state.keycmd, 0);
2225 parse_command(act->name, act->param, NULL);
2229 /* try if it's an incremental keycmd or one that takes args, and run it */
2230 GString* short_keys = g_string_new ("");
2231 GString* short_keys_inc = g_string_new ("");
2233 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2234 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2235 g_string_assign(short_keys_inc, short_keys->str);
2236 g_string_append_c(short_keys, '_');
2237 g_string_append_c(short_keys_inc, '*');
2239 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2240 /* run normal cmds only if return was pressed */
2241 exec_paramcmd(act, i);
2242 g_string_truncate(uzbl.state.keycmd, 0);
2244 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2245 if (key_ret) /* just quit the incremental command on return */
2246 g_string_truncate(uzbl.state.keycmd, 0);
2247 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2251 g_string_truncate(short_keys, short_keys->len - 1);
2253 g_string_free (short_keys, TRUE);
2254 g_string_free (short_keys_inc, TRUE);
2258 exec_paramcmd(const Action *act, const guint i) {
2259 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2260 GString *actionname = g_string_new ("");
2261 GString *actionparam = g_string_new ("");
2262 g_string_erase (parampart, 0, i+1);
2264 g_string_printf (actionname, act->name, parampart->str);
2266 g_string_printf (actionparam, act->param, parampart->str);
2267 parse_command(actionname->str, actionparam->str, NULL);
2268 g_string_free(actionname, TRUE);
2269 g_string_free(actionparam, TRUE);
2270 g_string_free(parampart, TRUE);
2278 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2279 //main_window_ref = g_object_ref(scrolled_window);
2280 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
2282 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2283 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2285 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2286 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2287 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2288 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2289 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2290 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2291 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2292 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2293 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2294 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2295 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2297 return scrolled_window;
2304 g->mainbar = gtk_hbox_new (FALSE, 0);
2306 /* keep a reference to the bar so we can re-pack it at runtime*/
2307 //sbar_ref = g_object_ref(g->mainbar);
2309 g->mainbar_label = gtk_label_new ("");
2310 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2311 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2312 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2313 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2314 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2315 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2321 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2322 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2323 gtk_widget_set_name (window, "Uzbl browser");
2324 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2325 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2332 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2333 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2334 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2341 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2343 If actname is one that calls an external command, this function will inject
2344 newargs in front of the user-provided args in that command line. They will
2345 come become after the body of the script (in sh) or after the name of
2346 the command to execute (in spawn).
2347 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2348 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2350 The return value consist of two strings: the action (sh, ...) and its args.
2352 If act is not one that calls an external command, then the given action merely
2355 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2356 /* Arrr! Here be memory leaks */
2357 gchar *actdup = g_strdup(actname);
2358 g_array_append_val(rets, actdup);
2360 if ((g_strcmp0(actname, "spawn") == 0) ||
2361 (g_strcmp0(actname, "sh") == 0) ||
2362 (g_strcmp0(actname, "sync_spawn") == 0) ||
2363 (g_strcmp0(actname, "sync_sh") == 0)) {
2365 GString *a = g_string_new("");
2366 gchar **spawnparts = split_quoted(origargs, FALSE);
2367 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2368 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2370 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2371 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2373 g_array_append_val(rets, a->str);
2374 g_string_free(a, FALSE);
2375 g_strfreev(spawnparts);
2377 gchar *origdup = g_strdup(origargs);
2378 g_array_append_val(rets, origdup);
2380 return (gchar**)g_array_free(rets, FALSE);
2384 run_handler (const gchar *act, const gchar *args) {
2385 /* Consider this code a temporary hack to make the handlers usable.
2386 In practice, all this splicing, injection, and reconstruction is
2387 inefficient, annoying and hard to manage. Potential pitfalls arise
2388 when the handler specific args 1) are not quoted (the handler
2389 callbacks should take care of this) 2) are quoted but interfere
2390 with the users' own quotation. A more ideal solution is
2391 to refactor parse_command so that it doesn't just take a string
2392 and execute it; rather than that, we should have a function which
2393 returns the argument vector parsed from the string. This vector
2394 could be modified (e.g. insert additional args into it) before
2395 passing it to the next function that actually executes it. Though
2396 it still isn't perfect for chain actions.. will reconsider & re-
2397 factor when I have the time. -duc */
2399 char **parts = g_strsplit(act, " ", 2);
2401 if (g_strcmp0(parts[0], "chain") == 0) {
2402 GString *newargs = g_string_new("");
2403 gchar **chainparts = split_quoted(parts[1], FALSE);
2405 /* for every argument in the chain, inject the handler args
2406 and make sure the new parts are wrapped in quotes */
2407 gchar **cp = chainparts;
2409 gchar *quotless = NULL;
2410 gchar **spliced_quotless = NULL; // sigh -_-;
2411 gchar **inpart = NULL;
2414 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2416 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2417 } else quotless = g_strdup(*cp);
2419 spliced_quotless = g_strsplit(quotless, " ", 2);
2420 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2421 g_strfreev(spliced_quotless);
2423 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2429 parse_command(parts[0], &(newargs->str[1]), NULL);
2430 g_string_free(newargs, TRUE);
2431 g_strfreev(chainparts);
2434 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2435 parse_command(inparts[0], inparts[1], NULL);
2443 add_binding (const gchar *key, const gchar *act) {
2444 char **parts = g_strsplit(act, " ", 2);
2451 if (uzbl.state.verbose)
2452 printf ("Binding %-10s : %s\n", key, act);
2453 action = new_action(parts[0], parts[1]);
2455 if (g_hash_table_remove (uzbl.bindings, key))
2456 g_warning ("Overwriting existing binding for \"%s\"", key);
2457 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2462 get_xdg_var (XDG_Var xdg) {
2463 const gchar* actual_value = getenv (xdg.environmental);
2464 const gchar* home = getenv ("HOME");
2465 gchar* return_value;
2467 if (! actual_value || strcmp (actual_value, "") == 0) {
2468 if (xdg.default_value) {
2469 return_value = str_replace ("~", home, xdg.default_value);
2471 return_value = NULL;
2474 return_value = str_replace("~", home, actual_value);
2477 return return_value;
2481 find_xdg_file (int xdg_type, char* filename) {
2482 /* xdg_type = 0 => config
2483 xdg_type = 1 => data
2484 xdg_type = 2 => cache*/
2486 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2487 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2490 gchar* temporary_string;
2494 if (! file_exists (temporary_file) && xdg_type != 2) {
2495 buf = get_xdg_var (XDG[3 + xdg_type]);
2496 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2499 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2500 g_free (temporary_file);
2501 temporary_file = g_strconcat (temporary_string, filename, NULL);
2505 //g_free (temporary_string); - segfaults.
2507 if (file_exists (temporary_file)) {
2508 return temporary_file;
2515 State *s = &uzbl.state;
2516 Network *n = &uzbl.net;
2518 for (i = 0; default_config[i].command != NULL; i++) {
2519 parse_cmd_line(default_config[i].command, NULL);
2522 if (g_strcmp0(s->config_file, "-") == 0) {
2523 s->config_file = NULL;
2527 else if (!s->config_file) {
2528 s->config_file = find_xdg_file (0, "/uzbl/config");
2531 if (s->config_file) {
2532 GArray* lines = read_file_by_line (s->config_file);
2536 while ((line = g_array_index(lines, gchar*, i))) {
2537 parse_cmd_line (line, NULL);
2541 g_array_free (lines, TRUE);
2543 if (uzbl.state.verbose)
2544 printf ("No configuration file loaded.\n");
2547 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2550 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2553 if (!uzbl.behave.cookie_handler)
2556 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2557 GString *s = g_string_new ("");
2558 SoupURI * soup_uri = soup_message_get_uri(msg);
2559 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2560 run_handler(uzbl.behave.cookie_handler, s->str);
2562 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2563 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2564 if ( p != NULL ) *p = '\0';
2565 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2567 if (uzbl.comm.sync_stdout)
2568 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2570 g_string_free(s, TRUE);
2574 save_cookies (SoupMessage *msg, gpointer user_data){
2578 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2579 cookie = soup_cookie_to_set_cookie_header(ck->data);
2580 SoupURI * soup_uri = soup_message_get_uri(msg);
2581 GString *s = g_string_new ("");
2582 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2583 run_handler(uzbl.behave.cookie_handler, s->str);
2585 g_string_free(s, TRUE);
2590 /* --- WEBINSPECTOR --- */
2592 hide_window_cb(GtkWidget *widget, gpointer data) {
2595 gtk_widget_hide(widget);
2599 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2602 (void) web_inspector;
2603 GtkWidget* scrolled_window;
2604 GtkWidget* new_web_view;
2607 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2608 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2609 G_CALLBACK(hide_window_cb), NULL);
2611 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2612 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2613 gtk_widget_show(g->inspector_window);
2615 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2616 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2617 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2618 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2619 gtk_widget_show(scrolled_window);
2621 new_web_view = webkit_web_view_new();
2622 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2624 return WEBKIT_WEB_VIEW(new_web_view);
2628 inspector_show_window_cb (WebKitWebInspector* inspector){
2630 gtk_widget_show(uzbl.gui.inspector_window);
2634 /* TODO: Add variables and code to make use of these functions */
2636 inspector_close_window_cb (WebKitWebInspector* inspector){
2642 inspector_attach_window_cb (WebKitWebInspector* inspector){
2648 inspector_detach_window_cb (WebKitWebInspector* inspector){
2654 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2660 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2666 set_up_inspector() {
2668 WebKitWebSettings *settings = view_settings();
2669 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2671 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2672 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2673 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2674 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2675 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2676 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2677 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2679 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2683 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2685 uzbl_cmdprop *c = v;
2690 if(c->type == TYPE_STR)
2691 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2692 else if(c->type == TYPE_INT)
2693 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2694 else if(c->type == TYPE_FLOAT)
2695 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2699 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2703 printf("bind %s = %s %s\n", (char *)k ,
2704 (char *)a->name, a->param?(char *)a->param:"");
2709 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2710 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2713 #ifndef UZBL_LIBRARY
2716 main (int argc, char* argv[]) {
2717 gtk_init (&argc, &argv);
2718 if (!g_thread_supported ())
2719 g_thread_init (NULL);
2720 uzbl.state.executable_path = g_strdup(argv[0]);
2721 uzbl.state.selected_url = NULL;
2722 uzbl.state.searchtx = NULL;
2724 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2725 g_option_context_add_main_entries (context, entries, NULL);
2726 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2727 g_option_context_parse (context, &argc, &argv, NULL);
2728 g_option_context_free(context);
2730 if (uzbl.behave.print_version) {
2731 printf("Commit: %s\n", COMMIT);
2735 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2736 if (argc > 1 && !uzbl.state.uri)
2737 uri_override = g_strdup(argv[1]);
2738 gboolean verbose_override = uzbl.state.verbose;
2740 /* initialize hash table */
2741 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2743 uzbl.net.soup_session = webkit_get_default_session();
2744 uzbl.state.keycmd = g_string_new("");
2746 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2747 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2748 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2749 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2750 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2751 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2754 if(uname(&uzbl.state.unameinfo) == -1)
2755 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2757 uzbl.gui.sbar.progress_s = g_strdup("=");
2758 uzbl.gui.sbar.progress_u = g_strdup("·");
2759 uzbl.gui.sbar.progress_w = 10;
2761 /* HTML mode defaults*/
2762 uzbl.behave.html_buffer = g_string_new("");
2763 uzbl.behave.html_endmarker = g_strdup(".");
2764 uzbl.behave.html_timeout = 60;
2765 uzbl.behave.base_url = g_strdup("http://invalid");
2767 /* default mode indicators */
2768 uzbl.behave.insert_indicator = g_strdup("I");
2769 uzbl.behave.cmd_indicator = g_strdup("C");
2773 make_var_to_name_hash();
2775 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2777 uzbl.gui.scrolled_win = create_browser();
2780 /* initial packing */
2781 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2782 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2784 if (uzbl.state.socket_id) {
2785 uzbl.gui.plug = create_plug ();
2786 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2787 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2789 uzbl.gui.main_window = create_window ();
2790 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2791 gtk_widget_show_all (uzbl.gui.main_window);
2792 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2795 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2797 if (uzbl.state.verbose) {
2798 printf("Uzbl start location: %s\n", argv[0]);
2799 if (uzbl.state.socket_id)
2800 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2802 printf("window_id %i\n",(int) uzbl.xwin);
2803 printf("pid %i\n", getpid ());
2804 printf("name: %s\n", uzbl.state.instance_name);
2807 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2808 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2809 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2810 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2811 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2815 if (!uzbl.behave.show_status)
2816 gtk_widget_hide(uzbl.gui.mainbar);
2823 if (verbose_override > uzbl.state.verbose)
2824 uzbl.state.verbose = verbose_override;
2827 set_var_value("uri", uri_override);
2828 g_free(uri_override);
2829 } else if (uzbl.state.uri)
2830 cmd_load_uri(uzbl.gui.web_view, NULL);
2835 return EXIT_SUCCESS;
2839 /* vi: set et ts=4: */