1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
90 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
92 /* abbreviations to help keep the table's width humane */
93 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
94 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* ---------------------------------------------------------------------------------------------- */
102 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
104 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
105 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
106 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
107 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
108 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
109 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
110 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
111 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
112 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
113 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
114 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
115 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
116 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
117 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_title)},
118 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
119 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
120 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
121 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
122 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, NULL)},
123 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
124 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
125 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
126 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
127 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
128 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
129 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
130 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
131 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
132 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
133 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
134 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
135 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
136 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
137 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
138 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
139 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
140 /* exported WebKitWebSettings properties */
141 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
142 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
143 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
144 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
145 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
146 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
147 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
148 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
149 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
150 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
151 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
152 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
153 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
154 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
155 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
156 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
158 /* constants (not dumpable or writeable) */
159 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
160 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
161 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
162 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
163 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
165 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
166 }, *n2v_p = var_name_to_ptr;
173 { "SHIFT", GDK_SHIFT_MASK }, // shift
174 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
175 { "CONTROL", GDK_CONTROL_MASK }, // control
176 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
177 { "MOD2", GDK_MOD2_MASK }, // 5th mod
178 { "MOD3", GDK_MOD3_MASK }, // 6th mod
179 { "MOD4", GDK_MOD4_MASK }, // 7th mod
180 { "MOD5", GDK_MOD5_MASK }, // 8th mod
181 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
182 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
183 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
184 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
185 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
186 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
187 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
188 { "META", GDK_META_MASK }, // meta (since 2.10)
193 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
194 * for quick access */
196 make_var_to_name_hash() {
197 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
199 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
204 /* --- UTILITY FUNCTIONS --- */
206 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
208 get_exp_type(gchar *s) {
212 else if(*(s+1) == '{')
213 return EXP_BRACED_VAR;
214 else if(*(s+1) == '<')
217 return EXP_SIMPLE_VAR;
223 * recurse == 1: don't expand '@(command)@'
224 * recurse == 2: don't expand '@<java script>@'
227 expand(char *s, guint recurse, gboolean escape_markup) {
231 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
236 gchar *cmd_stdout = NULL;
238 GString *buf = g_string_new("");
239 GString *js_ret = g_string_new("");
244 g_string_append_c(buf, *++s);
249 etype = get_exp_type(s);
254 if( (vend = strpbrk(s, end_simple_var)) ||
255 (vend = strchr(s, '\0')) ) {
256 strncpy(ret, s, vend-s);
262 if( (vend = strchr(s, upto)) ||
263 (vend = strchr(s, '\0')) ) {
264 strncpy(ret, s, vend-s);
270 strcpy(str_end, ")@");
272 if( (vend = strstr(s, str_end)) ||
273 (vend = strchr(s, '\0')) ) {
274 strncpy(ret, s, vend-s);
280 strcpy(str_end, ">@");
282 if( (vend = strstr(s, str_end)) ||
283 (vend = strchr(s, '\0')) ) {
284 strncpy(ret, s, vend-s);
290 if(etype == EXP_SIMPLE_VAR ||
291 etype == EXP_BRACED_VAR) {
292 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
293 if(c->type == TYPE_STR) {
295 char *b = g_markup_escape_text((gchar *)*c->ptr,
296 strlen((gchar *)*c->ptr));
297 g_string_append(buf, b);
300 g_string_append(buf, (gchar *)*c->ptr);
302 } else if(c && c->type == TYPE_INT) {
303 char *b = itos((uintptr_t)*c->ptr);
304 g_string_append(buf, b);
309 if(etype == EXP_SIMPLE_VAR)
314 else if(recurse != 1 &&
316 mycmd = expand(ret, 1, escape_markup);
317 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
321 g_printerr("error on running command: %s\n", err->message);
324 else if (*cmd_stdout) {
326 char *b = g_markup_escape_text(cmd_stdout,
328 g_string_append(buf, b);
331 g_string_append(buf, cmd_stdout);
337 else if(recurse != 2 &&
339 mycmd = expand(ret, 2, escape_markup);
340 eval_js(uzbl.gui.web_view, mycmd, js_ret);
345 char *b = g_markup_escape_text(js_ret->str,
346 strlen(js_ret->str));
347 g_string_append(buf, b);
350 g_string_append(buf, js_ret->str);
352 g_string_free(js_ret, TRUE);
353 js_ret = g_string_new("");
360 g_string_append_c(buf, *s);
365 g_string_free(js_ret, TRUE);
366 return g_string_free(buf, FALSE);
373 snprintf(tmp, sizeof(tmp), "%i", val);
374 return g_strdup(tmp);
378 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
381 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
384 str_replace (const char* search, const char* replace, const char* string) {
388 buf = g_strsplit (string, search, -1);
389 ret = g_strjoinv (replace, buf);
390 g_strfreev(buf); // somebody said this segfaults
396 read_file_by_line (gchar *path) {
397 GIOChannel *chan = NULL;
398 gchar *readbuf = NULL;
400 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
403 chan = g_io_channel_new_file(path, "r", NULL);
406 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
407 const gchar* val = g_strdup (readbuf);
408 g_array_append_val (lines, val);
413 g_io_channel_unref (chan);
415 fprintf(stderr, "File '%s' not be read.\n", path);
422 gchar* parseenv (char* string) {
423 extern char** environ;
424 gchar* tmpstr = NULL;
428 while (environ[i] != NULL) {
429 gchar** env = g_strsplit (environ[i], "=", 2);
430 gchar* envname = g_strconcat ("$", env[0], NULL);
432 if (g_strrstr (string, envname) != NULL) {
433 tmpstr = g_strdup(string);
435 string = str_replace(envname, env[1], tmpstr);
440 g_strfreev (env); // somebody said this breaks uzbl
448 setup_signal(int signr, sigfunc *shandler) {
449 struct sigaction nh, oh;
451 nh.sa_handler = shandler;
452 sigemptyset(&nh.sa_mask);
455 if(sigaction(signr, &nh, &oh) < 0)
463 if (uzbl.behave.fifo_dir)
464 unlink (uzbl.comm.fifo_path);
465 if (uzbl.behave.socket_dir)
466 unlink (uzbl.comm.socket_path);
468 g_free(uzbl.state.executable_path);
469 g_string_free(uzbl.state.keycmd, TRUE);
470 g_hash_table_destroy(uzbl.bindings);
471 g_hash_table_destroy(uzbl.behave.commands);
474 /* used for html_mode_timeout
475 * be sure to extend this function to use
476 * more timers if needed in other places
479 set_timeout(int seconds) {
481 memset(&t, 0, sizeof t);
483 t.it_value.tv_sec = seconds;
484 t.it_value.tv_usec = 0;
485 setitimer(ITIMER_REAL, &t, NULL);
488 /* --- SIGNAL HANDLER --- */
491 catch_sigterm(int s) {
497 catch_sigint(int s) {
507 set_var_value("mode", "0");
512 /* --- CALLBACKS --- */
515 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
518 (void) navigation_action;
519 (void) policy_decision;
521 const gchar* uri = webkit_network_request_get_uri (request);
522 if (uzbl.state.verbose)
523 printf("New window requested -> %s \n", uri);
524 new_window_load_uri(uri);
529 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
534 /* If we can display it, let's display it... */
535 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
536 webkit_web_policy_decision_use (policy_decision);
540 /* ...everything we can't displayed is downloaded */
541 webkit_web_policy_decision_download (policy_decision);
546 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
550 if (uzbl.state.selected_url != NULL) {
551 if (uzbl.state.verbose)
552 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
553 new_window_load_uri(uzbl.state.selected_url);
555 if (uzbl.state.verbose)
556 printf("New web view -> %s\n","Nothing to open, exiting");
562 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
565 if (uzbl.behave.download_handler) {
566 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
567 if (uzbl.state.verbose)
568 printf("Download -> %s\n",uri);
569 /* if urls not escaped, we may have to escape and quote uri before this call */
570 run_handler(uzbl.behave.download_handler, uri);
575 /* scroll a bar in a given direction */
577 scroll (GtkAdjustment* bar, GArray *argv) {
581 gdouble page_size = gtk_adjustment_get_page_size(bar);
582 gdouble value = gtk_adjustment_get_value(bar);
583 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
586 value += page_size * amount * 0.01;
590 max_value = gtk_adjustment_get_upper(bar) - page_size;
592 if (value > max_value)
593 value = max_value; /* don't scroll past the end of the page */
595 gtk_adjustment_set_value (bar, value);
599 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
600 (void) page; (void) argv; (void) result;
601 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
605 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
606 (void) page; (void) argv; (void) result;
607 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
608 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
612 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
613 (void) page; (void) result;
614 scroll(uzbl.gui.bar_v, argv);
618 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
619 (void) page; (void) result;
620 scroll(uzbl.gui.bar_h, argv);
625 if (!uzbl.behave.show_status) {
626 gtk_widget_hide(uzbl.gui.mainbar);
628 gtk_widget_show(uzbl.gui.mainbar);
634 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
639 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
643 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
648 if (uzbl.behave.show_status) {
649 gtk_widget_hide(uzbl.gui.mainbar);
651 gtk_widget_show(uzbl.gui.mainbar);
653 uzbl.behave.show_status = !uzbl.behave.show_status;
658 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
662 //Set selected_url state variable
663 g_free(uzbl.state.selected_url);
664 uzbl.state.selected_url = NULL;
666 uzbl.state.selected_url = g_strdup(link);
672 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
675 const gchar *title = webkit_web_view_get_title(web_view);
676 if (uzbl.gui.main_title)
677 g_free (uzbl.gui.main_title);
678 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
683 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
686 uzbl.gui.sbar.load_progress = progress;
691 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
695 if (uzbl.behave.load_finish_handler)
696 run_handler(uzbl.behave.load_finish_handler, "");
700 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
704 uzbl.gui.sbar.load_progress = 0;
705 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
706 if (uzbl.behave.load_start_handler)
707 run_handler(uzbl.behave.load_start_handler, "");
711 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
714 g_free (uzbl.state.uri);
715 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
716 uzbl.state.uri = g_string_free (newuri, FALSE);
717 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
718 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
721 if (uzbl.behave.load_commit_handler)
722 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
726 destroy_cb (GtkWidget* widget, gpointer data) {
734 if (uzbl.behave.history_handler) {
736 struct tm * timeinfo;
739 timeinfo = localtime ( &rawtime );
740 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
741 run_handler(uzbl.behave.history_handler, date);
746 /* VIEW funcs (little webkit wrappers) */
747 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
749 VIEWFUNC(reload_bypass_cache)
750 VIEWFUNC(stop_loading)
757 /* -- command to callback/function map for things we cannot attach to any signals */
758 static struct {char *key; CommandInfo value;} cmdlist[] =
759 { /* key function no_split */
760 { "back", {view_go_back, 0} },
761 { "forward", {view_go_forward, 0} },
762 { "scroll_vert", {scroll_vert, 0} },
763 { "scroll_horz", {scroll_horz, 0} },
764 { "scroll_begin", {scroll_begin, 0} },
765 { "scroll_end", {scroll_end, 0} },
766 { "reload", {view_reload, 0}, },
767 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
768 { "stop", {view_stop_loading, 0}, },
769 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
770 { "zoom_out", {view_zoom_out, 0}, },
771 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
772 { "uri", {load_uri, TRUE} },
773 { "js", {run_js, TRUE} },
774 { "script", {run_external_js, 0} },
775 { "toggle_status", {toggle_status_cb, 0} },
776 { "spawn", {spawn, 0} },
777 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
778 { "sh", {spawn_sh, 0} },
779 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
780 { "exit", {close_uzbl, 0} },
781 { "search", {search_forward_text, TRUE} },
782 { "search_reverse", {search_reverse_text, TRUE} },
783 { "dehilight", {dehilight, 0} },
784 { "toggle_insert_mode", {toggle_insert_mode, 0} },
785 { "set", {set_var, TRUE} },
786 //{ "get", {get_var, TRUE} },
787 { "bind", {act_bind, TRUE} },
788 { "dump_config", {act_dump_config, 0} },
789 { "keycmd", {keycmd, TRUE} },
790 { "keycmd_nl", {keycmd_nl, TRUE} },
791 { "keycmd_bs", {keycmd_bs, 0} },
792 { "chain", {chain, 0} },
793 { "print", {print, TRUE} }
800 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
802 for (i = 0; i < LENGTH(cmdlist); i++)
803 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
806 /* -- CORE FUNCTIONS -- */
809 free_action(gpointer act) {
810 Action *action = (Action*)act;
811 g_free(action->name);
813 g_free(action->param);
818 new_action(const gchar *name, const gchar *param) {
819 Action *action = g_new(Action, 1);
821 action->name = g_strdup(name);
823 action->param = g_strdup(param);
825 action->param = NULL;
831 file_exists (const char * filename) {
832 return (access(filename, F_OK) == 0);
836 set_var(WebKitWebView *page, GArray *argv, GString *result) {
837 (void) page; (void) result;
838 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
839 if (split[0] != NULL) {
840 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
841 set_var_value(g_strstrip(split[0]), value);
848 print(WebKitWebView *page, GArray *argv, GString *result) {
849 (void) page; (void) result;
852 buf = expand(argv_idx(argv, 0), 0, FALSE);
853 g_string_assign(result, buf);
858 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
859 (void) page; (void) result;
860 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
861 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
862 add_binding(g_strstrip(split[0]), value);
874 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
875 (void) page; (void) result;
877 if (argv_idx(argv, 0)) {
878 if (strcmp (argv_idx(argv, 0), "0") == 0) {
879 uzbl.behave.insert_mode = FALSE;
881 uzbl.behave.insert_mode = TRUE;
884 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
891 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
894 if (argv_idx(argv, 0)) {
895 GString* newuri = g_string_new (argv_idx(argv, 0));
896 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
897 run_js(web_view, argv, NULL);
900 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
901 g_string_prepend (newuri, "http://");
902 /* if we do handle cookies, ask our handler for them */
903 webkit_web_view_load_uri (web_view, newuri->str);
904 g_string_free (newuri, TRUE);
912 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
913 size_t argumentCount, const JSValueRef arguments[],
914 JSValueRef* exception) {
919 JSStringRef js_result_string;
920 GString *result = g_string_new("");
922 if (argumentCount >= 1) {
923 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
924 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
925 char ctl_line[arg_size];
926 JSStringGetUTF8CString(arg, ctl_line, arg_size);
928 parse_cmd_line(ctl_line, result);
930 JSStringRelease(arg);
932 js_result_string = JSStringCreateWithUTF8CString(result->str);
934 g_string_free(result, TRUE);
936 return JSValueMakeString(ctx, js_result_string);
939 static JSStaticFunction js_static_functions[] = {
940 {"run", js_run_command, kJSPropertyAttributeNone},
945 /* This function creates the class and its definition, only once */
946 if (!uzbl.js.initialized) {
947 /* it would be pretty cool to make this dynamic */
948 uzbl.js.classdef = kJSClassDefinitionEmpty;
949 uzbl.js.classdef.staticFunctions = js_static_functions;
951 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
957 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
958 WebKitWebFrame *frame;
959 JSGlobalContextRef context;
960 JSObjectRef globalobject;
961 JSStringRef var_name;
963 JSStringRef js_script;
964 JSValueRef js_result;
965 JSStringRef js_result_string;
966 size_t js_result_size;
970 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
971 context = webkit_web_frame_get_global_context(frame);
972 globalobject = JSContextGetGlobalObject(context);
974 /* uzbl javascript namespace */
975 var_name = JSStringCreateWithUTF8CString("Uzbl");
976 JSObjectSetProperty(context, globalobject, var_name,
977 JSObjectMake(context, uzbl.js.classref, NULL),
978 kJSClassAttributeNone, NULL);
980 /* evaluate the script and get return value*/
981 js_script = JSStringCreateWithUTF8CString(script);
982 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
983 if (js_result && !JSValueIsUndefined(context, js_result)) {
984 js_result_string = JSValueToStringCopy(context, js_result, NULL);
985 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
987 if (js_result_size) {
988 char js_result_utf8[js_result_size];
989 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
990 g_string_assign(result, js_result_utf8);
993 JSStringRelease(js_result_string);
997 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
999 JSStringRelease(var_name);
1000 JSStringRelease(js_script);
1004 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1006 if (argv_idx(argv, 0))
1007 eval_js(web_view, argv_idx(argv, 0), result);
1011 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1013 if (argv_idx(argv, 0)) {
1014 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1019 while ((line = g_array_index(lines, gchar*, i))) {
1021 js = g_strdup (line);
1023 gchar* newjs = g_strconcat (js, line, NULL);
1030 if (uzbl.state.verbose)
1031 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1033 if (argv_idx (argv, 1)) {
1034 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1038 eval_js (web_view, js, result);
1040 g_array_free (lines, TRUE);
1045 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1046 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1047 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1048 webkit_web_view_unmark_text_matches (page);
1049 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1050 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1054 if (uzbl.state.searchtx) {
1055 if (uzbl.state.verbose)
1056 printf ("Searching: %s\n", uzbl.state.searchtx);
1057 webkit_web_view_set_highlight_text_matches (page, TRUE);
1058 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1063 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1065 search_text(page, argv, TRUE);
1069 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1071 search_text(page, argv, FALSE);
1075 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1076 (void) argv; (void) result;
1077 webkit_web_view_set_highlight_text_matches (page, FALSE);
1082 new_window_load_uri (const gchar * uri) {
1083 GString* to_execute = g_string_new ("");
1084 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1086 for (i = 0; entries[i].long_name != NULL; i++) {
1087 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1088 gchar** str = (gchar**)entries[i].arg_data;
1090 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1094 if (uzbl.state.verbose)
1095 printf("\n%s\n", to_execute->str);
1096 g_spawn_command_line_async (to_execute->str, NULL);
1097 g_string_free (to_execute, TRUE);
1101 chain (WebKitWebView *page, GArray *argv, GString *result) {
1102 (void) page; (void) result;
1104 gchar **parts = NULL;
1106 while ((a = argv_idx(argv, i++))) {
1107 parts = g_strsplit (a, " ", 2);
1108 parse_command(parts[0], parts[1], result);
1114 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1118 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1124 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1128 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1134 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1139 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1141 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1146 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1153 /* --Statusbar functions-- */
1155 build_progressbar_ascii(int percent) {
1156 int width=uzbl.gui.sbar.progress_w;
1159 GString *bar = g_string_new("");
1161 l = (double)percent*((double)width/100.);
1162 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1164 for(i=0; i<(int)l; i++)
1165 g_string_append(bar, uzbl.gui.sbar.progress_s);
1168 g_string_append(bar, uzbl.gui.sbar.progress_u);
1170 return g_string_free(bar, FALSE);
1175 const GScannerConfig scan_config = {
1178 ) /* cset_skip_characters */,
1183 ) /* cset_identifier_first */,
1190 ) /* cset_identifier_nth */,
1191 ( "" ) /* cpair_comment_single */,
1193 TRUE /* case_sensitive */,
1195 FALSE /* skip_comment_multi */,
1196 FALSE /* skip_comment_single */,
1197 FALSE /* scan_comment_multi */,
1198 TRUE /* scan_identifier */,
1199 TRUE /* scan_identifier_1char */,
1200 FALSE /* scan_identifier_NULL */,
1201 TRUE /* scan_symbols */,
1202 FALSE /* scan_binary */,
1203 FALSE /* scan_octal */,
1204 FALSE /* scan_float */,
1205 FALSE /* scan_hex */,
1206 FALSE /* scan_hex_dollar */,
1207 FALSE /* scan_string_sq */,
1208 FALSE /* scan_string_dq */,
1209 TRUE /* numbers_2_int */,
1210 FALSE /* int_2_float */,
1211 FALSE /* identifier_2_string */,
1212 FALSE /* char_2_token */,
1213 FALSE /* symbol_2_token */,
1214 TRUE /* scope_0_fallback */,
1219 uzbl.scan = g_scanner_new(&scan_config);
1220 while(symp->symbol_name) {
1221 g_scanner_scope_add_symbol(uzbl.scan, 0,
1223 GINT_TO_POINTER(symp->symbol_token));
1229 expand_template(const char *template, gboolean escape_markup) {
1230 if(!template) return NULL;
1232 GTokenType token = G_TOKEN_NONE;
1233 GString *ret = g_string_new("");
1237 g_scanner_input_text(uzbl.scan, template, strlen(template));
1238 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1239 token = g_scanner_get_next_token(uzbl.scan);
1241 if(token == G_TOKEN_SYMBOL) {
1242 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1246 buf = uzbl.state.uri?
1247 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1248 g_string_append(ret, buf);
1252 g_string_append(ret, uzbl.state.uri?
1253 uzbl.state.uri:g_strdup(""));
1256 buf = itos(uzbl.gui.sbar.load_progress);
1257 g_string_append(ret, buf);
1260 case SYM_LOADPRGSBAR:
1261 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1262 g_string_append(ret, buf);
1267 buf = uzbl.gui.main_title?
1268 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1269 g_string_append(ret, buf);
1273 g_string_append(ret, uzbl.gui.main_title?
1274 uzbl.gui.main_title:g_strdup(""));
1276 case SYM_SELECTED_URI:
1278 buf = uzbl.state.selected_url?
1279 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1280 g_string_append(ret, buf);
1284 g_string_append(ret, uzbl.state.selected_url?
1285 uzbl.state.selected_url:g_strdup(""));
1288 buf = itos(uzbl.xwin);
1289 g_string_append(ret,
1290 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1295 buf = uzbl.state.keycmd->str?
1296 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1297 g_string_append(ret, buf);
1301 g_string_append(ret, uzbl.state.keycmd->str?
1302 uzbl.state.keycmd->str:g_strdup(""));
1305 g_string_append(ret,
1306 uzbl.behave.insert_mode?
1307 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1310 g_string_append(ret,
1311 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1313 /* useragent syms */
1318 else if(token == G_TOKEN_INT) {
1319 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1320 g_string_append(ret, buf);
1323 else if(token == G_TOKEN_IDENTIFIER) {
1324 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1326 else if(token == G_TOKEN_CHAR) {
1327 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1331 return g_string_free(ret, FALSE);
1333 /* --End Statusbar functions-- */
1336 sharg_append(GArray *a, const gchar *str) {
1337 const gchar *s = (str ? str : "");
1338 g_array_append_val(a, s);
1341 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1343 run_command (const gchar *command, const guint npre, const gchar **args,
1344 const gboolean sync, char **output_stdout) {
1345 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1348 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1349 gchar *pid = itos(getpid());
1350 gchar *xwin = itos(uzbl.xwin);
1352 sharg_append(a, command);
1353 for (i = 0; i < npre; i++) /* add n args before the default vars */
1354 sharg_append(a, args[i]);
1355 sharg_append(a, uzbl.state.config_file);
1356 sharg_append(a, pid);
1357 sharg_append(a, xwin);
1358 sharg_append(a, uzbl.comm.fifo_path);
1359 sharg_append(a, uzbl.comm.socket_path);
1360 sharg_append(a, uzbl.state.uri);
1361 sharg_append(a, uzbl.gui.main_title);
1363 for (i = npre; i < g_strv_length((gchar**)args); i++)
1364 sharg_append(a, args[i]);
1368 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1370 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1371 NULL, NULL, output_stdout, NULL, NULL, &err);
1372 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1373 NULL, NULL, NULL, &err);
1375 if (uzbl.state.verbose) {
1376 GString *s = g_string_new("spawned:");
1377 for (i = 0; i < (a->len); i++) {
1378 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1379 g_string_append_printf(s, " %s", qarg);
1382 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1383 printf("%s\n", s->str);
1384 g_string_free(s, TRUE);
1386 printf("Stdout: %s\n", *output_stdout);
1390 g_printerr("error on run_command: %s\n", err->message);
1395 g_array_free (a, TRUE);
1400 split_quoted(const gchar* src, const gboolean unquote) {
1401 /* split on unquoted space, return array of strings;
1402 remove a layer of quotes and backslashes if unquote */
1403 if (!src) return NULL;
1405 gboolean dq = FALSE;
1406 gboolean sq = FALSE;
1407 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1408 GString *s = g_string_new ("");
1412 for (p = src; *p != '\0'; p++) {
1413 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1414 else if (*p == '\\') { g_string_append_c(s, *p++);
1415 g_string_append_c(s, *p); }
1416 else if ((*p == '"') && unquote && !sq) dq = !dq;
1417 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1419 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1420 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1422 else if ((*p == ' ') && !dq && !sq) {
1423 dup = g_strdup(s->str);
1424 g_array_append_val(a, dup);
1425 g_string_truncate(s, 0);
1426 } else g_string_append_c(s, *p);
1428 dup = g_strdup(s->str);
1429 g_array_append_val(a, dup);
1430 ret = (gchar**)a->data;
1431 g_array_free (a, FALSE);
1432 g_string_free (s, TRUE);
1437 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1438 (void)web_view; (void)result;
1439 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1440 if (argv_idx(argv, 0))
1441 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1445 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1446 (void)web_view; (void)result;
1448 if (argv_idx(argv, 0))
1449 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1450 TRUE, &uzbl.comm.sync_stdout);
1454 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1455 (void)web_view; (void)result;
1456 if (!uzbl.behave.shell_cmd) {
1457 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1462 gchar *spacer = g_strdup("");
1463 g_array_insert_val(argv, 1, spacer);
1464 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1466 for (i = 1; i < g_strv_length(cmd); i++)
1467 g_array_prepend_val(argv, cmd[i]);
1469 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1475 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1476 (void)web_view; (void)result;
1477 if (!uzbl.behave.shell_cmd) {
1478 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1483 gchar *spacer = g_strdup("");
1484 g_array_insert_val(argv, 1, spacer);
1485 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1487 for (i = 1; i < g_strv_length(cmd); i++)
1488 g_array_prepend_val(argv, cmd[i]);
1490 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1491 TRUE, &uzbl.comm.sync_stdout);
1497 parse_command(const char *cmd, const char *param, GString *result) {
1500 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1502 gchar **par = split_quoted(param, TRUE);
1503 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1505 if (c->no_split) { /* don't split */
1506 sharg_append(a, param);
1508 for (i = 0; i < g_strv_length(par); i++)
1509 sharg_append(a, par[i]);
1512 if (result == NULL) {
1513 GString *result_print = g_string_new("");
1515 c->function(uzbl.gui.web_view, a, result_print);
1516 if (result_print->len)
1517 printf("%*s\n", result_print->len, result_print->str);
1519 g_string_free(result_print, TRUE);
1521 c->function(uzbl.gui.web_view, a, result);
1524 g_array_free (a, TRUE);
1527 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1534 if(*uzbl.net.proxy_url == ' '
1535 || uzbl.net.proxy_url == NULL) {
1536 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1537 (GType) SOUP_SESSION_PROXY_URI);
1540 suri = soup_uri_new(uzbl.net.proxy_url);
1541 g_object_set(G_OBJECT(uzbl.net.soup_session),
1542 SOUP_SESSION_PROXY_URI,
1544 soup_uri_free(suri);
1551 if(file_exists(uzbl.gui.icon)) {
1552 if (uzbl.gui.main_window)
1553 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1555 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1557 g_free (uzbl.gui.icon);
1562 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1563 g_array_append_val (a, uzbl.state.uri);
1564 load_uri(uzbl.gui.web_view, a, NULL);
1565 g_array_free (a, TRUE);
1569 cmd_always_insert_mode() {
1570 uzbl.behave.insert_mode =
1571 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1577 g_object_set(G_OBJECT(uzbl.net.soup_session),
1578 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1582 cmd_max_conns_host() {
1583 g_object_set(G_OBJECT(uzbl.net.soup_session),
1584 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1589 soup_session_remove_feature
1590 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1591 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1592 /*g_free(uzbl.net.soup_logger);*/
1594 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1595 soup_session_add_feature(uzbl.net.soup_session,
1596 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1599 static WebKitWebSettings*
1601 return webkit_web_view_get_settings(uzbl.gui.web_view);
1606 WebKitWebSettings *ws = view_settings();
1607 if (uzbl.behave.font_size > 0) {
1608 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1611 if (uzbl.behave.monospace_size > 0) {
1612 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1613 uzbl.behave.monospace_size, NULL);
1615 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1616 uzbl.behave.font_size, NULL);
1622 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1626 cmd_disable_plugins() {
1627 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1628 !uzbl.behave.disable_plugins, NULL);
1632 cmd_disable_scripts() {
1633 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1634 !uzbl.behave.disable_scripts, NULL);
1638 cmd_minimum_font_size() {
1639 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1640 uzbl.behave.minimum_font_size, NULL);
1643 cmd_autoload_img() {
1644 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1645 uzbl.behave.autoload_img, NULL);
1650 cmd_autoshrink_img() {
1651 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1652 uzbl.behave.autoshrink_img, NULL);
1657 cmd_enable_spellcheck() {
1658 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1659 uzbl.behave.enable_spellcheck, NULL);
1663 cmd_enable_private() {
1664 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1665 uzbl.behave.enable_private, NULL);
1670 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1671 uzbl.behave.print_bg, NULL);
1676 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1677 uzbl.behave.style_uri, NULL);
1681 cmd_resizable_txt() {
1682 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1683 uzbl.behave.resizable_txt, NULL);
1687 cmd_default_encoding() {
1688 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1689 uzbl.behave.default_encoding, NULL);
1693 cmd_enforce_96dpi() {
1694 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1695 uzbl.behave.enforce_96dpi, NULL);
1699 cmd_caret_browsing() {
1700 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1701 uzbl.behave.caret_browsing, NULL);
1705 cmd_cookie_handler() {
1706 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1707 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1708 if ((g_strcmp0(split[0], "sh") == 0) ||
1709 (g_strcmp0(split[0], "spawn") == 0)) {
1710 g_free (uzbl.behave.cookie_handler);
1711 uzbl.behave.cookie_handler =
1712 g_strdup_printf("sync_%s %s", split[0], split[1]);
1719 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1724 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1729 if(uzbl.behave.inject_html) {
1730 webkit_web_view_load_html_string (uzbl.gui.web_view,
1731 uzbl.behave.inject_html, NULL);
1740 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1741 uzbl.behave.modmask = 0;
1743 if(uzbl.behave.modkey)
1744 g_free(uzbl.behave.modkey);
1745 uzbl.behave.modkey = buf;
1747 for (i = 0; modkeys[i].key != NULL; i++) {
1748 if (g_strrstr(buf, modkeys[i].key))
1749 uzbl.behave.modmask |= modkeys[i].mask;
1755 if (*uzbl.net.useragent == ' ') {
1756 g_free (uzbl.net.useragent);
1757 uzbl.net.useragent = NULL;
1759 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1760 uzbl.net.useragent, NULL);
1766 gtk_widget_ref(uzbl.gui.scrolled_win);
1767 gtk_widget_ref(uzbl.gui.mainbar);
1768 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1769 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1771 if(uzbl.behave.status_top) {
1772 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1773 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1776 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1777 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1779 gtk_widget_unref(uzbl.gui.scrolled_win);
1780 gtk_widget_unref(uzbl.gui.mainbar);
1781 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1786 set_var_value(gchar *name, gchar *val) {
1787 uzbl_cmdprop *c = NULL;
1791 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1792 if(!c->writeable) return TRUE;
1794 /* check for the variable type */
1795 if (c->type == TYPE_STR) {
1796 buf = expand(val, 0, FALSE);
1799 } else if(c->type == TYPE_INT) {
1800 int *ip = (int *)c->ptr;
1801 buf = expand(val, 0, FALSE);
1802 *ip = (int)strtoul(buf, &endp, 10);
1804 } else if (c->type == TYPE_FLOAT) {
1805 float *fp = (float *)c->ptr;
1806 buf = expand(val, 0, FALSE);
1807 *fp = strtod(buf, &endp);
1811 /* invoke a command specific function */
1812 if(c->func) c->func();
1819 Behaviour *b = &uzbl.behave;
1821 if(b->html_buffer->str) {
1822 webkit_web_view_load_html_string (uzbl.gui.web_view,
1823 b->html_buffer->str, b->base_url);
1824 g_string_free(b->html_buffer, TRUE);
1825 b->html_buffer = g_string_new("");
1829 enum {M_CMD, M_HTML};
1831 parse_cmd_line(const char *ctl_line, GString *result) {
1832 Behaviour *b = &uzbl.behave;
1835 if(b->mode == M_HTML) {
1836 len = strlen(b->html_endmarker);
1837 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1838 if(len == strlen(ctl_line)-1 &&
1839 !strncmp(b->html_endmarker, ctl_line, len)) {
1841 set_var_value("mode", "0");
1846 set_timeout(b->html_timeout);
1847 g_string_append(b->html_buffer, ctl_line);
1850 else if((ctl_line[0] == '#') /* Comments */
1851 || (ctl_line[0] == ' ')
1852 || (ctl_line[0] == '\n'))
1853 ; /* ignore these lines */
1854 else { /* parse a command */
1856 gchar **tokens = NULL;
1857 len = strlen(ctl_line);
1859 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1860 ctlstrip = g_strndup(ctl_line, len - 1);
1861 else ctlstrip = g_strdup(ctl_line);
1863 tokens = g_strsplit(ctlstrip, " ", 2);
1864 parse_command(tokens[0], tokens[1], result);
1871 build_stream_name(int type, const gchar* dir) {
1872 char *xwin_str = NULL;
1873 State *s = &uzbl.state;
1876 xwin_str = itos((int)uzbl.xwin);
1878 str = g_strdup_printf
1879 ("%s/uzbl_fifo_%s", dir,
1880 s->instance_name ? s->instance_name : xwin_str);
1881 } else if (type == SOCKET) {
1882 str = g_strdup_printf
1883 ("%s/uzbl_socket_%s", dir,
1884 s->instance_name ? s->instance_name : xwin_str );
1891 control_fifo(GIOChannel *gio, GIOCondition condition) {
1892 if (uzbl.state.verbose)
1893 printf("triggered\n");
1898 if (condition & G_IO_HUP)
1899 g_error ("Fifo: Read end of pipe died!\n");
1902 g_error ("Fifo: GIOChannel broke\n");
1904 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1905 if (ret == G_IO_STATUS_ERROR) {
1906 g_error ("Fifo: Error reading: %s\n", err->message);
1910 parse_cmd_line(ctl_line, NULL);
1917 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1918 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1919 if (unlink(uzbl.comm.fifo_path) == -1)
1920 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1921 g_free(uzbl.comm.fifo_path);
1922 uzbl.comm.fifo_path = NULL;
1925 if (*dir == ' ') { /* space unsets the variable */
1930 GIOChannel *chan = NULL;
1931 GError *error = NULL;
1932 gchar *path = build_stream_name(FIFO, dir);
1934 if (!file_exists(path)) {
1935 if (mkfifo (path, 0666) == 0) {
1936 // 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.
1937 chan = g_io_channel_new_file(path, "r+", &error);
1939 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1940 if (uzbl.state.verbose)
1941 printf ("init_fifo: created successfully as %s\n", path);
1942 uzbl.comm.fifo_path = path;
1944 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1945 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1946 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1947 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1949 /* if we got this far, there was an error; cleanup */
1950 if (error) g_error_free (error);
1957 control_stdin(GIOChannel *gio, GIOCondition condition) {
1959 gchar *ctl_line = NULL;
1962 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1963 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1966 parse_cmd_line(ctl_line, NULL);
1974 GIOChannel *chan = NULL;
1975 GError *error = NULL;
1977 chan = g_io_channel_unix_new(fileno(stdin));
1979 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1980 g_error ("Stdin: could not add watch\n");
1982 if (uzbl.state.verbose)
1983 printf ("Stdin: watch added successfully\n");
1986 g_error ("Stdin: Error while opening: %s\n", error->message);
1988 if (error) g_error_free (error);
1992 control_socket(GIOChannel *chan) {
1993 struct sockaddr_un remote;
1994 unsigned int t = sizeof(remote);
1996 GIOChannel *clientchan;
1998 clientsock = accept (g_io_channel_unix_get_fd(chan),
1999 (struct sockaddr *) &remote, &t);
2001 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2002 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2003 (GIOFunc) control_client_socket, clientchan);
2010 control_client_socket(GIOChannel *clientchan) {
2012 GString *result = g_string_new("");
2013 GError *error = NULL;
2017 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2018 if (ret == G_IO_STATUS_ERROR) {
2019 g_warning ("Error reading: %s\n", error->message);
2020 g_io_channel_shutdown(clientchan, TRUE, &error);
2022 } else if (ret == G_IO_STATUS_EOF) {
2023 /* shutdown and remove channel watch from main loop */
2024 g_io_channel_shutdown(clientchan, TRUE, &error);
2029 parse_cmd_line (ctl_line, result);
2030 g_string_append_c(result, '\n');
2031 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2033 if (ret == G_IO_STATUS_ERROR) {
2034 g_warning ("Error writing: %s", error->message);
2036 g_io_channel_flush(clientchan, &error);
2039 if (error) g_error_free (error);
2040 g_string_free(result, TRUE);
2046 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2047 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2048 if (unlink(uzbl.comm.socket_path) == -1)
2049 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2050 g_free(uzbl.comm.socket_path);
2051 uzbl.comm.socket_path = NULL;
2059 GIOChannel *chan = NULL;
2061 struct sockaddr_un local;
2062 gchar *path = build_stream_name(SOCKET, dir);
2064 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2066 local.sun_family = AF_UNIX;
2067 strcpy (local.sun_path, path);
2068 unlink (local.sun_path);
2070 len = strlen (local.sun_path) + sizeof (local.sun_family);
2071 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2072 if (uzbl.state.verbose)
2073 printf ("init_socket: opened in %s\n", path);
2076 if( (chan = g_io_channel_unix_new(sock)) ) {
2077 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2078 uzbl.comm.socket_path = path;
2081 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2083 /* if we got this far, there was an error; cleanup */
2090 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2091 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2093 // this function may be called very early when the templates are not set (yet), hence the checks
2095 update_title (void) {
2096 Behaviour *b = &uzbl.behave;
2099 if (b->show_status) {
2100 if (b->title_format_short) {
2101 parsed = expand_template(b->title_format_short, FALSE);
2102 if (uzbl.gui.main_window)
2103 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2106 if (b->status_format) {
2107 parsed = expand_template(b->status_format, TRUE);
2108 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2111 if (b->status_background) {
2113 gdk_color_parse (b->status_background, &color);
2114 //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2115 if (uzbl.gui.main_window)
2116 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2117 else if (uzbl.gui.plug)
2118 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2121 if (b->title_format_long) {
2122 parsed = expand_template(b->title_format_long, FALSE);
2123 if (uzbl.gui.main_window)
2124 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2131 key_press_cb (GtkWidget* window, GdkEventKey* event)
2133 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2137 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2138 || 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)
2141 /* turn off insert mode (if always_insert_mode is not used) */
2142 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2143 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2148 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2151 if (event->keyval == GDK_Escape) {
2152 g_string_truncate(uzbl.state.keycmd, 0);
2154 dehilight(uzbl.gui.web_view, NULL, NULL);
2158 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2159 if (event->keyval == GDK_Insert) {
2161 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2162 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2164 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2167 g_string_append (uzbl.state.keycmd, str);
2174 if (event->keyval == GDK_BackSpace)
2175 keycmd_bs(NULL, NULL, NULL);
2177 gboolean key_ret = FALSE;
2178 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2180 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2182 run_keycmd(key_ret);
2184 if (key_ret) return (!uzbl.behave.insert_mode);
2189 run_keycmd(const gboolean key_ret) {
2190 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2192 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2193 g_string_truncate(uzbl.state.keycmd, 0);
2194 parse_command(act->name, act->param, NULL);
2198 /* try if it's an incremental keycmd or one that takes args, and run it */
2199 GString* short_keys = g_string_new ("");
2200 GString* short_keys_inc = g_string_new ("");
2202 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2203 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2204 g_string_assign(short_keys_inc, short_keys->str);
2205 g_string_append_c(short_keys, '_');
2206 g_string_append_c(short_keys_inc, '*');
2208 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2209 /* run normal cmds only if return was pressed */
2210 exec_paramcmd(act, i);
2211 g_string_truncate(uzbl.state.keycmd, 0);
2213 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2214 if (key_ret) /* just quit the incremental command on return */
2215 g_string_truncate(uzbl.state.keycmd, 0);
2216 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2220 g_string_truncate(short_keys, short_keys->len - 1);
2222 g_string_free (short_keys, TRUE);
2223 g_string_free (short_keys_inc, TRUE);
2227 exec_paramcmd(const Action *act, const guint i) {
2228 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2229 GString *actionname = g_string_new ("");
2230 GString *actionparam = g_string_new ("");
2231 g_string_erase (parampart, 0, i+1);
2233 g_string_printf (actionname, act->name, parampart->str);
2235 g_string_printf (actionparam, act->param, parampart->str);
2236 parse_command(actionname->str, actionparam->str, NULL);
2237 g_string_free(actionname, TRUE);
2238 g_string_free(actionparam, TRUE);
2239 g_string_free(parampart, TRUE);
2247 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2248 //main_window_ref = g_object_ref(scrolled_window);
2249 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
2251 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2252 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2254 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2255 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2256 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2257 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2258 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2259 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2266 return scrolled_window;
2273 g->mainbar = gtk_hbox_new (FALSE, 0);
2275 /* keep a reference to the bar so we can re-pack it at runtime*/
2276 //sbar_ref = g_object_ref(g->mainbar);
2278 g->mainbar_label = gtk_label_new ("");
2279 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2280 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2281 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2282 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2283 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2284 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2289 GtkWidget* create_window () {
2290 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2291 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2292 gtk_widget_set_name (window, "Uzbl browser");
2293 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2294 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2300 GtkPlug* create_plug () {
2301 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2302 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2303 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2310 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2312 If actname is one that calls an external command, this function will inject
2313 newargs in front of the user-provided args in that command line. They will
2314 come become after the body of the script (in sh) or after the name of
2315 the command to execute (in spawn).
2316 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2317 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2319 The return value consist of two strings: the action (sh, ...) and its args.
2321 If act is not one that calls an external command, then the given action merely
2324 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2325 gchar *actdup = g_strdup(actname);
2326 g_array_append_val(rets, actdup);
2328 if ((g_strcmp0(actname, "spawn") == 0) ||
2329 (g_strcmp0(actname, "sh") == 0) ||
2330 (g_strcmp0(actname, "sync_spawn") == 0) ||
2331 (g_strcmp0(actname, "sync_sh") == 0)) {
2333 GString *a = g_string_new("");
2334 gchar **spawnparts = split_quoted(origargs, FALSE);
2335 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2336 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2338 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2339 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2341 g_array_append_val(rets, a->str);
2342 g_string_free(a, FALSE);
2343 g_strfreev(spawnparts);
2345 gchar *origdup = g_strdup(origargs);
2346 g_array_append_val(rets, origdup);
2348 return (gchar**)g_array_free(rets, FALSE);
2352 run_handler (const gchar *act, const gchar *args) {
2353 /* Consider this code a temporary hack to make the handlers usable.
2354 In practice, all this splicing, injection, and reconstruction is
2355 inefficient, annoying and hard to manage. Potential pitfalls arise
2356 when the handler specific args 1) are not quoted (the handler
2357 callbacks should take care of this) 2) are quoted but interfere
2358 with the users' own quotation. A more ideal solution is
2359 to refactor parse_command so that it doesn't just take a string
2360 and execute it; rather than that, we should have a function which
2361 returns the argument vector parsed from the string. This vector
2362 could be modified (e.g. insert additional args into it) before
2363 passing it to the next function that actually executes it. Though
2364 it still isn't perfect for chain actions.. will reconsider & re-
2365 factor when I have the time. -duc */
2367 char **parts = g_strsplit(act, " ", 2);
2369 if (g_strcmp0(parts[0], "chain") == 0) {
2370 GString *newargs = g_string_new("");
2371 gchar **chainparts = split_quoted(parts[1], FALSE);
2373 /* for every argument in the chain, inject the handler args
2374 and make sure the new parts are wrapped in quotes */
2375 gchar **cp = chainparts;
2377 gchar *quotless = NULL;
2378 gchar **spliced_quotless = NULL; // sigh -_-;
2379 gchar **inpart = NULL;
2382 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2384 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2385 } else quotless = g_strdup(*cp);
2387 spliced_quotless = g_strsplit(quotless, " ", 2);
2388 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2389 g_strfreev(spliced_quotless);
2391 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2397 parse_command(parts[0], &(newargs->str[1]), NULL);
2398 g_string_free(newargs, TRUE);
2399 g_strfreev(chainparts);
2402 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2403 parse_command(inparts[0], inparts[1], NULL);
2411 add_binding (const gchar *key, const gchar *act) {
2412 char **parts = g_strsplit(act, " ", 2);
2419 if (uzbl.state.verbose)
2420 printf ("Binding %-10s : %s\n", key, act);
2421 action = new_action(parts[0], parts[1]);
2423 if (g_hash_table_remove (uzbl.bindings, key))
2424 g_warning ("Overwriting existing binding for \"%s\"", key);
2425 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2430 get_xdg_var (XDG_Var xdg) {
2431 const gchar* actual_value = getenv (xdg.environmental);
2432 const gchar* home = getenv ("HOME");
2433 gchar* return_value;
2435 if (! actual_value || strcmp (actual_value, "") == 0) {
2436 if (xdg.default_value) {
2437 return_value = str_replace ("~", home, xdg.default_value);
2439 return_value = NULL;
2442 return_value = str_replace("~", home, actual_value);
2445 return return_value;
2449 find_xdg_file (int xdg_type, char* filename) {
2450 /* xdg_type = 0 => config
2451 xdg_type = 1 => data
2452 xdg_type = 2 => cache*/
2454 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2455 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2458 gchar* temporary_string;
2462 if (! file_exists (temporary_file) && xdg_type != 2) {
2463 buf = get_xdg_var (XDG[3 + xdg_type]);
2464 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2467 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2468 g_free (temporary_file);
2469 temporary_file = g_strconcat (temporary_string, filename, NULL);
2473 //g_free (temporary_string); - segfaults.
2475 if (file_exists (temporary_file)) {
2476 return temporary_file;
2483 State *s = &uzbl.state;
2484 Network *n = &uzbl.net;
2486 for (i = 0; default_config[i].command != NULL; i++) {
2487 parse_cmd_line(default_config[i].command, NULL);
2490 if (g_strcmp0(s->config_file, "-") == 0) {
2491 s->config_file = NULL;
2495 else if (!s->config_file) {
2496 s->config_file = find_xdg_file (0, "/uzbl/config");
2499 if (s->config_file) {
2500 GArray* lines = read_file_by_line (s->config_file);
2504 while ((line = g_array_index(lines, gchar*, i))) {
2505 parse_cmd_line (line, NULL);
2509 g_array_free (lines, TRUE);
2511 if (uzbl.state.verbose)
2512 printf ("No configuration file loaded.\n");
2515 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2518 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2521 if (!uzbl.behave.cookie_handler)
2524 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2525 GString *s = g_string_new ("");
2526 SoupURI * soup_uri = soup_message_get_uri(msg);
2527 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2528 run_handler(uzbl.behave.cookie_handler, s->str);
2530 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2531 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2532 if ( p != NULL ) *p = '\0';
2533 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2535 if (uzbl.comm.sync_stdout)
2536 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2538 g_string_free(s, TRUE);
2542 save_cookies (SoupMessage *msg, gpointer user_data){
2546 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2547 cookie = soup_cookie_to_set_cookie_header(ck->data);
2548 SoupURI * soup_uri = soup_message_get_uri(msg);
2549 GString *s = g_string_new ("");
2550 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2551 run_handler(uzbl.behave.cookie_handler, s->str);
2553 g_string_free(s, TRUE);
2558 /* --- WEBINSPECTOR --- */
2560 hide_window_cb(GtkWidget *widget, gpointer data) {
2563 gtk_widget_hide(widget);
2566 static WebKitWebView*
2567 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2570 (void) web_inspector;
2571 GtkWidget* scrolled_window;
2572 GtkWidget* new_web_view;
2575 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2576 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2577 G_CALLBACK(hide_window_cb), NULL);
2579 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2580 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2581 gtk_widget_show(g->inspector_window);
2583 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2584 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2585 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2586 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2587 gtk_widget_show(scrolled_window);
2589 new_web_view = webkit_web_view_new();
2590 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2592 return WEBKIT_WEB_VIEW(new_web_view);
2596 inspector_show_window_cb (WebKitWebInspector* inspector){
2598 gtk_widget_show(uzbl.gui.inspector_window);
2602 /* TODO: Add variables and code to make use of these functions */
2604 inspector_close_window_cb (WebKitWebInspector* inspector){
2610 inspector_attach_window_cb (WebKitWebInspector* inspector){
2616 inspector_detach_window_cb (WebKitWebInspector* inspector){
2622 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2628 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2634 set_up_inspector() {
2636 WebKitWebSettings *settings = view_settings();
2637 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2639 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2640 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2641 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2642 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2643 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2644 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2645 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2647 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2651 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2653 uzbl_cmdprop *c = v;
2658 if(c->type == TYPE_STR)
2659 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2660 else if(c->type == TYPE_INT)
2661 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2665 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2669 printf("bind %s = %s %s\n", (char *)k ,
2670 (char *)a->name, a->param?(char *)a->param:"");
2675 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2676 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2679 /* set up gtk, gobject, variable defaults and other things that tests and other
2680 * external applications need to do anyhow */
2682 initialize(int argc, char *argv[]) {
2683 gtk_init (&argc, &argv);
2684 if (!g_thread_supported ())
2685 g_thread_init (NULL);
2686 uzbl.state.executable_path = g_strdup(argv[0]);
2687 uzbl.state.selected_url = NULL;
2688 uzbl.state.searchtx = NULL;
2690 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2691 g_option_context_add_main_entries (context, entries, NULL);
2692 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2693 g_option_context_parse (context, &argc, &argv, NULL);
2694 g_option_context_free(context);
2696 /* initialize hash table */
2697 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2699 uzbl.net.soup_session = webkit_get_default_session();
2700 uzbl.state.keycmd = g_string_new("");
2702 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2703 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2704 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2705 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2706 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2707 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2709 uzbl.gui.sbar.progress_s = g_strdup("=");
2710 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2711 uzbl.gui.sbar.progress_w = 10;
2713 /* HTML mode defaults*/
2714 uzbl.behave.html_buffer = g_string_new("");
2715 uzbl.behave.html_endmarker = g_strdup(".");
2716 uzbl.behave.html_timeout = 60;
2717 uzbl.behave.base_url = g_strdup("http://invalid");
2719 /* default mode indicators */
2720 uzbl.behave.insert_indicator = g_strdup("I");
2721 uzbl.behave.cmd_indicator = g_strdup("C");
2723 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2724 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2725 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2726 uzbl.info.arch = ARCH;
2727 uzbl.info.commit = COMMIT;
2731 make_var_to_name_hash();
2733 uzbl.gui.scrolled_win = create_browser();
2736 #ifndef UZBL_LIBRARY
2739 main (int argc, char* argv[]) {
2740 initialize(argc, argv);
2742 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2746 /* initial packing */
2747 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2748 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2750 if (uzbl.state.socket_id) {
2751 uzbl.gui.plug = create_plug ();
2752 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2753 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2755 uzbl.gui.main_window = create_window ();
2756 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2757 gtk_widget_show_all (uzbl.gui.main_window);
2758 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2761 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2763 if (uzbl.state.verbose) {
2764 printf("Uzbl start location: %s\n", argv[0]);
2765 if (uzbl.state.socket_id)
2766 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2768 printf("window_id %i\n",(int) uzbl.xwin);
2769 printf("pid %i\n", getpid ());
2770 printf("name: %s\n", uzbl.state.instance_name);
2773 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2774 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2775 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2776 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2777 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2781 if (!uzbl.behave.show_status)
2782 gtk_widget_hide(uzbl.gui.mainbar);
2789 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2790 gboolean verbose_override = uzbl.state.verbose;
2792 if (verbose_override > uzbl.state.verbose)
2793 uzbl.state.verbose = verbose_override;
2796 set_var_value("uri", uri_override);
2797 g_free(uri_override);
2798 } else if (uzbl.state.uri)
2799 cmd_load_uri(uzbl.gui.web_view, NULL);
2804 return EXIT_SUCCESS;
2808 /* vi: set et ts=4: */