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 {
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
165 } const_name_to_ptr[] = {
166 { "WEBKIT_MAJOR", {(void*)WEBKIT_MAJOR_VERSION, TYPE_INT}},
167 { "WEBKIT_MINOR", {(void*)WEBKIT_MINOR_VERSION, TYPE_INT}},
168 { "WEBKIT_MICRO", {(void*)WEBKIT_MICRO_VERSION, TYPE_INT}},
169 { "SYSNAME", {&(uzbl.state.unameinfo.sysname), TYPE_STR}},
170 { "NODENAME", {&(uzbl.state.unameinfo.nodename), TYPE_STR}},
171 { "KERNREL", {&(uzbl.state.unameinfo.release), TYPE_STR}},
172 { "KERNVER", {&(uzbl.state.unameinfo.version), TYPE_STR}},
173 { "ARCH_SYSTEM", {&(uzbl.state.unameinfo.machine), TYPE_STR}},
174 { "ARCH_UZBL", {&(ARCH), TYPE_STR}},
176 { "DOMAINNAME", {&(uzbl.state.unameinfo.domainname), TYPE_STR}},
178 { "COMMIT", {&(COMMIT), TYPE_STR}},
180 { NULL, {NULL, TYPE_INT}}
181 }, *n2c_p = const_name_to_ptr;
187 { "SHIFT", GDK_SHIFT_MASK }, // shift
188 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
189 { "CONTROL", GDK_CONTROL_MASK }, // control
190 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
191 { "MOD2", GDK_MOD2_MASK }, // 5th mod
192 { "MOD3", GDK_MOD3_MASK }, // 6th mod
193 { "MOD4", GDK_MOD4_MASK }, // 7th mod
194 { "MOD5", GDK_MOD5_MASK }, // 8th mod
195 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
196 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
197 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
198 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
199 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
200 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
201 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
202 { "META", GDK_META_MASK }, // meta (since 2.10)
207 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
208 * for quick access */
210 make_var_to_name_hash() {
211 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
213 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
217 uzbl.comm.proto_const = g_hash_table_new(g_str_hash, g_str_equal);
219 g_hash_table_insert(uzbl.comm.proto_const, n2c_p->name, (gpointer) &n2c_p->cp);
224 /* --- UTILITY FUNCTIONS --- */
226 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
228 get_exp_type(gchar *s) {
232 else if(*(s+1) == '{')
233 return EXP_BRACED_VAR;
234 else if(*(s+1) == '<')
237 return EXP_SIMPLE_VAR;
243 * recurse == 1: don't expand '@(command)@'
244 * recurse == 2: don't expand '@<java script>@'
247 expand(char *s, guint recurse, gboolean escape_markup) {
251 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
256 gchar *cmd_stdout = NULL;
258 GString *buf = g_string_new("");
259 GString *js_ret = g_string_new("");
264 g_string_append_c(buf, *++s);
269 etype = get_exp_type(s);
274 if( (vend = strpbrk(s, end_simple_var)) ||
275 (vend = strchr(s, '\0')) ) {
276 strncpy(ret, s, vend-s);
282 if( (vend = strchr(s, upto)) ||
283 (vend = strchr(s, '\0')) ) {
284 strncpy(ret, s, vend-s);
290 strcpy(str_end, ")@");
292 if( (vend = strstr(s, str_end)) ||
293 (vend = strchr(s, '\0')) ) {
294 strncpy(ret, s, vend-s);
300 strcpy(str_end, ">@");
302 if( (vend = strstr(s, str_end)) ||
303 (vend = strchr(s, '\0')) ) {
304 strncpy(ret, s, vend-s);
310 if(etype == EXP_SIMPLE_VAR ||
311 etype == EXP_BRACED_VAR) {
314 if('A' <= ret[0] && 'Z' >= ret[0] &&
315 (c = g_hash_table_lookup(uzbl.comm.proto_const, ret))) {
317 } else if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
321 if(c && c->type == TYPE_STR) {
323 char *b = g_markup_escape_text((gchar *)ptr,
324 strlen((gchar *)ptr));
325 g_string_append(buf, b);
328 g_string_append(buf, (gchar *)ptr);
330 } else if(c && c->type == TYPE_INT) {
331 char *b = itos((uintptr_t)ptr);
332 g_string_append(buf, b);
336 if(etype == EXP_SIMPLE_VAR)
341 else if(recurse != 1 &&
343 mycmd = expand(ret, 1, escape_markup);
344 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
348 g_printerr("error on running command: %s\n", err->message);
351 else if (*cmd_stdout) {
353 char *b = g_markup_escape_text(cmd_stdout,
355 g_string_append(buf, b);
358 g_string_append(buf, cmd_stdout);
364 else if(recurse != 2 &&
366 mycmd = expand(ret, 2, escape_markup);
367 eval_js(uzbl.gui.web_view, mycmd, js_ret);
372 char *b = g_markup_escape_text(js_ret->str,
373 strlen(js_ret->str));
374 g_string_append(buf, b);
377 g_string_append(buf, js_ret->str);
379 g_string_free(js_ret, TRUE);
380 js_ret = g_string_new("");
387 g_string_append_c(buf, *s);
392 g_string_free(js_ret, TRUE);
393 return g_string_free(buf, FALSE);
400 snprintf(tmp, sizeof(tmp), "%i", val);
401 return g_strdup(tmp);
405 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
408 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
411 str_replace (const char* search, const char* replace, const char* string) {
415 buf = g_strsplit (string, search, -1);
416 ret = g_strjoinv (replace, buf);
417 g_strfreev(buf); // somebody said this segfaults
423 read_file_by_line (gchar *path) {
424 GIOChannel *chan = NULL;
425 gchar *readbuf = NULL;
427 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
430 chan = g_io_channel_new_file(path, "r", NULL);
433 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
434 const gchar* val = g_strdup (readbuf);
435 g_array_append_val (lines, val);
440 g_io_channel_unref (chan);
442 fprintf(stderr, "File '%s' not be read.\n", path);
449 gchar* parseenv (char* string) {
450 extern char** environ;
451 gchar* tmpstr = NULL;
455 while (environ[i] != NULL) {
456 gchar** env = g_strsplit (environ[i], "=", 2);
457 gchar* envname = g_strconcat ("$", env[0], NULL);
459 if (g_strrstr (string, envname) != NULL) {
460 tmpstr = g_strdup(string);
462 string = str_replace(envname, env[1], tmpstr);
467 g_strfreev (env); // somebody said this breaks uzbl
475 setup_signal(int signr, sigfunc *shandler) {
476 struct sigaction nh, oh;
478 nh.sa_handler = shandler;
479 sigemptyset(&nh.sa_mask);
482 if(sigaction(signr, &nh, &oh) < 0)
490 if (uzbl.behave.fifo_dir)
491 unlink (uzbl.comm.fifo_path);
492 if (uzbl.behave.socket_dir)
493 unlink (uzbl.comm.socket_path);
495 g_free(uzbl.state.executable_path);
496 g_string_free(uzbl.state.keycmd, TRUE);
497 g_hash_table_destroy(uzbl.bindings);
498 g_hash_table_destroy(uzbl.behave.commands);
501 /* used for html_mode_timeout
502 * be sure to extend this function to use
503 * more timers if needed in other places
506 set_timeout(int seconds) {
508 memset(&t, 0, sizeof t);
510 t.it_value.tv_sec = seconds;
511 t.it_value.tv_usec = 0;
512 setitimer(ITIMER_REAL, &t, NULL);
515 /* --- SIGNAL HANDLER --- */
518 catch_sigterm(int s) {
524 catch_sigint(int s) {
534 set_var_value("mode", "0");
539 /* --- CALLBACKS --- */
542 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
545 (void) navigation_action;
546 (void) policy_decision;
548 const gchar* uri = webkit_network_request_get_uri (request);
549 if (uzbl.state.verbose)
550 printf("New window requested -> %s \n", uri);
551 new_window_load_uri(uri);
556 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
561 /* If we can display it, let's display it... */
562 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
563 webkit_web_policy_decision_use (policy_decision);
567 /* ...everything we can't displayed is downloaded */
568 webkit_web_policy_decision_download (policy_decision);
573 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
577 if (uzbl.state.selected_url != NULL) {
578 if (uzbl.state.verbose)
579 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
580 new_window_load_uri(uzbl.state.selected_url);
582 if (uzbl.state.verbose)
583 printf("New web view -> %s\n","Nothing to open, exiting");
589 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
592 if (uzbl.behave.download_handler) {
593 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
594 if (uzbl.state.verbose)
595 printf("Download -> %s\n",uri);
596 /* if urls not escaped, we may have to escape and quote uri before this call */
597 run_handler(uzbl.behave.download_handler, uri);
602 /* scroll a bar in a given direction */
604 scroll (GtkAdjustment* bar, GArray *argv) {
608 gdouble page_size = gtk_adjustment_get_page_size(bar);
609 gdouble value = gtk_adjustment_get_value(bar);
610 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
613 value += page_size * amount * 0.01;
617 max_value = gtk_adjustment_get_upper(bar) - page_size;
619 if (value > max_value)
620 value = max_value; /* don't scroll past the end of the page */
622 gtk_adjustment_set_value (bar, value);
626 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
627 (void) page; (void) argv; (void) result;
628 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
632 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
633 (void) page; (void) argv; (void) result;
634 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
635 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
639 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
640 (void) page; (void) result;
641 scroll(uzbl.gui.bar_v, argv);
645 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
646 (void) page; (void) result;
647 scroll(uzbl.gui.bar_h, argv);
652 if (!uzbl.behave.show_status) {
653 gtk_widget_hide(uzbl.gui.mainbar);
655 gtk_widget_show(uzbl.gui.mainbar);
661 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
666 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
670 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
675 if (uzbl.behave.show_status) {
676 gtk_widget_hide(uzbl.gui.mainbar);
678 gtk_widget_show(uzbl.gui.mainbar);
680 uzbl.behave.show_status = !uzbl.behave.show_status;
685 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
689 //Set selected_url state variable
690 g_free(uzbl.state.selected_url);
691 uzbl.state.selected_url = NULL;
693 uzbl.state.selected_url = g_strdup(link);
699 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
702 const gchar *title = webkit_web_view_get_title(web_view);
703 if (uzbl.gui.main_title)
704 g_free (uzbl.gui.main_title);
705 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
710 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
713 uzbl.gui.sbar.load_progress = progress;
718 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
722 if (uzbl.behave.load_finish_handler)
723 run_handler(uzbl.behave.load_finish_handler, "");
727 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
731 uzbl.gui.sbar.load_progress = 0;
732 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
733 if (uzbl.behave.load_start_handler)
734 run_handler(uzbl.behave.load_start_handler, "");
738 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
741 g_free (uzbl.state.uri);
742 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
743 uzbl.state.uri = g_string_free (newuri, FALSE);
744 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
745 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
748 if (uzbl.behave.load_commit_handler)
749 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
753 destroy_cb (GtkWidget* widget, gpointer data) {
761 if (uzbl.behave.history_handler) {
763 struct tm * timeinfo;
766 timeinfo = localtime ( &rawtime );
767 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
768 run_handler(uzbl.behave.history_handler, date);
773 /* VIEW funcs (little webkit wrappers) */
774 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
776 VIEWFUNC(reload_bypass_cache)
777 VIEWFUNC(stop_loading)
784 /* -- command to callback/function map for things we cannot attach to any signals */
785 static struct {char *key; CommandInfo value;} cmdlist[] =
786 { /* key function no_split */
787 { "back", {view_go_back, 0} },
788 { "forward", {view_go_forward, 0} },
789 { "scroll_vert", {scroll_vert, 0} },
790 { "scroll_horz", {scroll_horz, 0} },
791 { "scroll_begin", {scroll_begin, 0} },
792 { "scroll_end", {scroll_end, 0} },
793 { "reload", {view_reload, 0}, },
794 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
795 { "stop", {view_stop_loading, 0}, },
796 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
797 { "zoom_out", {view_zoom_out, 0}, },
798 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
799 { "uri", {load_uri, TRUE} },
800 { "js", {run_js, TRUE} },
801 { "script", {run_external_js, 0} },
802 { "toggle_status", {toggle_status_cb, 0} },
803 { "spawn", {spawn, 0} },
804 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
805 { "sh", {spawn_sh, 0} },
806 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
807 { "exit", {close_uzbl, 0} },
808 { "search", {search_forward_text, TRUE} },
809 { "search_reverse", {search_reverse_text, TRUE} },
810 { "dehilight", {dehilight, 0} },
811 { "toggle_insert_mode", {toggle_insert_mode, 0} },
812 { "set", {set_var, TRUE} },
813 //{ "get", {get_var, TRUE} },
814 { "bind", {act_bind, TRUE} },
815 { "dump_config", {act_dump_config, 0} },
816 { "keycmd", {keycmd, TRUE} },
817 { "keycmd_nl", {keycmd_nl, TRUE} },
818 { "keycmd_bs", {keycmd_bs, 0} },
819 { "chain", {chain, 0} },
820 { "print", {print, TRUE} }
827 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
829 for (i = 0; i < LENGTH(cmdlist); i++)
830 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
833 /* -- CORE FUNCTIONS -- */
836 free_action(gpointer act) {
837 Action *action = (Action*)act;
838 g_free(action->name);
840 g_free(action->param);
845 new_action(const gchar *name, const gchar *param) {
846 Action *action = g_new(Action, 1);
848 action->name = g_strdup(name);
850 action->param = g_strdup(param);
852 action->param = NULL;
858 file_exists (const char * filename) {
859 return (access(filename, F_OK) == 0);
863 set_var(WebKitWebView *page, GArray *argv, GString *result) {
864 (void) page; (void) result;
865 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
866 if (split[0] != NULL) {
867 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
868 set_var_value(g_strstrip(split[0]), value);
875 print(WebKitWebView *page, GArray *argv, GString *result) {
876 (void) page; (void) result;
879 buf = expand(argv_idx(argv, 0), 0, FALSE);
880 g_string_assign(result, buf);
885 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
886 (void) page; (void) result;
887 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
888 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
889 add_binding(g_strstrip(split[0]), value);
901 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
902 (void) page; (void) result;
904 if (argv_idx(argv, 0)) {
905 if (strcmp (argv_idx(argv, 0), "0") == 0) {
906 uzbl.behave.insert_mode = FALSE;
908 uzbl.behave.insert_mode = TRUE;
911 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
918 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
921 if (argv_idx(argv, 0)) {
922 GString* newuri = g_string_new (argv_idx(argv, 0));
923 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
924 run_js(web_view, argv, NULL);
927 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
928 g_string_prepend (newuri, "http://");
929 /* if we do handle cookies, ask our handler for them */
930 webkit_web_view_load_uri (web_view, newuri->str);
931 g_string_free (newuri, TRUE);
939 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
940 size_t argumentCount, const JSValueRef arguments[],
941 JSValueRef* exception) {
946 JSStringRef js_result_string;
947 GString *result = g_string_new("");
949 if (argumentCount >= 1) {
950 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
951 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
952 char ctl_line[arg_size];
953 JSStringGetUTF8CString(arg, ctl_line, arg_size);
955 parse_cmd_line(ctl_line, result);
957 JSStringRelease(arg);
959 js_result_string = JSStringCreateWithUTF8CString(result->str);
961 g_string_free(result, TRUE);
963 return JSValueMakeString(ctx, js_result_string);
966 static JSStaticFunction js_static_functions[] = {
967 {"run", js_run_command, kJSPropertyAttributeNone},
972 /* This function creates the class and its definition, only once */
973 if (!uzbl.js.initialized) {
974 /* it would be pretty cool to make this dynamic */
975 uzbl.js.classdef = kJSClassDefinitionEmpty;
976 uzbl.js.classdef.staticFunctions = js_static_functions;
978 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
984 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
985 WebKitWebFrame *frame;
986 JSGlobalContextRef context;
987 JSObjectRef globalobject;
988 JSStringRef var_name;
990 JSStringRef js_script;
991 JSValueRef js_result;
992 JSStringRef js_result_string;
993 size_t js_result_size;
997 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
998 context = webkit_web_frame_get_global_context(frame);
999 globalobject = JSContextGetGlobalObject(context);
1001 /* uzbl javascript namespace */
1002 var_name = JSStringCreateWithUTF8CString("Uzbl");
1003 JSObjectSetProperty(context, globalobject, var_name,
1004 JSObjectMake(context, uzbl.js.classref, NULL),
1005 kJSClassAttributeNone, NULL);
1007 /* evaluate the script and get return value*/
1008 js_script = JSStringCreateWithUTF8CString(script);
1009 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1010 if (js_result && !JSValueIsUndefined(context, js_result)) {
1011 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1012 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1014 if (js_result_size) {
1015 char js_result_utf8[js_result_size];
1016 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1017 g_string_assign(result, js_result_utf8);
1020 JSStringRelease(js_result_string);
1024 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1026 JSStringRelease(var_name);
1027 JSStringRelease(js_script);
1031 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1033 if (argv_idx(argv, 0))
1034 eval_js(web_view, argv_idx(argv, 0), result);
1038 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1040 if (argv_idx(argv, 0)) {
1041 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1046 while ((line = g_array_index(lines, gchar*, i))) {
1048 js = g_strdup (line);
1050 gchar* newjs = g_strconcat (js, line, NULL);
1057 if (uzbl.state.verbose)
1058 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1060 if (argv_idx (argv, 1)) {
1061 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1065 eval_js (web_view, js, result);
1067 g_array_free (lines, TRUE);
1072 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1073 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1074 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1075 webkit_web_view_unmark_text_matches (page);
1076 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1077 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1081 if (uzbl.state.searchtx) {
1082 if (uzbl.state.verbose)
1083 printf ("Searching: %s\n", uzbl.state.searchtx);
1084 webkit_web_view_set_highlight_text_matches (page, TRUE);
1085 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1090 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1092 search_text(page, argv, TRUE);
1096 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1098 search_text(page, argv, FALSE);
1102 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1103 (void) argv; (void) result;
1104 webkit_web_view_set_highlight_text_matches (page, FALSE);
1109 new_window_load_uri (const gchar * uri) {
1110 GString* to_execute = g_string_new ("");
1111 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1113 for (i = 0; entries[i].long_name != NULL; i++) {
1114 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1115 gchar** str = (gchar**)entries[i].arg_data;
1117 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1121 if (uzbl.state.verbose)
1122 printf("\n%s\n", to_execute->str);
1123 g_spawn_command_line_async (to_execute->str, NULL);
1124 g_string_free (to_execute, TRUE);
1128 chain (WebKitWebView *page, GArray *argv, GString *result) {
1129 (void) page; (void) result;
1131 gchar **parts = NULL;
1133 while ((a = argv_idx(argv, i++))) {
1134 parts = g_strsplit (a, " ", 2);
1135 parse_command(parts[0], parts[1], result);
1141 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1145 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1151 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1155 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1161 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1166 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1168 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1173 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1180 /* --Statusbar functions-- */
1182 build_progressbar_ascii(int percent) {
1183 int width=uzbl.gui.sbar.progress_w;
1186 GString *bar = g_string_new("");
1188 l = (double)percent*((double)width/100.);
1189 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1191 for(i=0; i<(int)l; i++)
1192 g_string_append(bar, uzbl.gui.sbar.progress_s);
1195 g_string_append(bar, uzbl.gui.sbar.progress_u);
1197 return g_string_free(bar, FALSE);
1202 const GScannerConfig scan_config = {
1205 ) /* cset_skip_characters */,
1210 ) /* cset_identifier_first */,
1217 ) /* cset_identifier_nth */,
1218 ( "" ) /* cpair_comment_single */,
1220 TRUE /* case_sensitive */,
1222 FALSE /* skip_comment_multi */,
1223 FALSE /* skip_comment_single */,
1224 FALSE /* scan_comment_multi */,
1225 TRUE /* scan_identifier */,
1226 TRUE /* scan_identifier_1char */,
1227 FALSE /* scan_identifier_NULL */,
1228 TRUE /* scan_symbols */,
1229 FALSE /* scan_binary */,
1230 FALSE /* scan_octal */,
1231 FALSE /* scan_float */,
1232 FALSE /* scan_hex */,
1233 FALSE /* scan_hex_dollar */,
1234 FALSE /* scan_string_sq */,
1235 FALSE /* scan_string_dq */,
1236 TRUE /* numbers_2_int */,
1237 FALSE /* int_2_float */,
1238 FALSE /* identifier_2_string */,
1239 FALSE /* char_2_token */,
1240 FALSE /* symbol_2_token */,
1241 TRUE /* scope_0_fallback */,
1246 uzbl.scan = g_scanner_new(&scan_config);
1247 while(symp->symbol_name) {
1248 g_scanner_scope_add_symbol(uzbl.scan, 0,
1250 GINT_TO_POINTER(symp->symbol_token));
1256 expand_template(const char *template, gboolean escape_markup) {
1257 if(!template) return NULL;
1259 GTokenType token = G_TOKEN_NONE;
1260 GString *ret = g_string_new("");
1264 g_scanner_input_text(uzbl.scan, template, strlen(template));
1265 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1266 token = g_scanner_get_next_token(uzbl.scan);
1268 if(token == G_TOKEN_SYMBOL) {
1269 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1273 buf = uzbl.state.uri?
1274 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1275 g_string_append(ret, buf);
1279 g_string_append(ret, uzbl.state.uri?
1280 uzbl.state.uri:g_strdup(""));
1283 buf = itos(uzbl.gui.sbar.load_progress);
1284 g_string_append(ret, buf);
1287 case SYM_LOADPRGSBAR:
1288 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1289 g_string_append(ret, buf);
1294 buf = uzbl.gui.main_title?
1295 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1296 g_string_append(ret, buf);
1300 g_string_append(ret, uzbl.gui.main_title?
1301 uzbl.gui.main_title:g_strdup(""));
1303 case SYM_SELECTED_URI:
1305 buf = uzbl.state.selected_url?
1306 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1307 g_string_append(ret, buf);
1311 g_string_append(ret, uzbl.state.selected_url?
1312 uzbl.state.selected_url:g_strdup(""));
1315 buf = itos(uzbl.xwin);
1316 g_string_append(ret,
1317 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1322 buf = uzbl.state.keycmd->str?
1323 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1324 g_string_append(ret, buf);
1328 g_string_append(ret, uzbl.state.keycmd->str?
1329 uzbl.state.keycmd->str:g_strdup(""));
1332 g_string_append(ret,
1333 uzbl.behave.insert_mode?
1334 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1337 g_string_append(ret,
1338 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1340 /* useragent syms */
1345 else if(token == G_TOKEN_INT) {
1346 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1347 g_string_append(ret, buf);
1350 else if(token == G_TOKEN_IDENTIFIER) {
1351 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1353 else if(token == G_TOKEN_CHAR) {
1354 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1358 return g_string_free(ret, FALSE);
1360 /* --End Statusbar functions-- */
1363 sharg_append(GArray *a, const gchar *str) {
1364 const gchar *s = (str ? str : "");
1365 g_array_append_val(a, s);
1368 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1370 run_command (const gchar *command, const guint npre, const gchar **args,
1371 const gboolean sync, char **output_stdout) {
1372 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1375 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1376 gchar *pid = itos(getpid());
1377 gchar *xwin = itos(uzbl.xwin);
1379 sharg_append(a, command);
1380 for (i = 0; i < npre; i++) /* add n args before the default vars */
1381 sharg_append(a, args[i]);
1382 sharg_append(a, uzbl.state.config_file);
1383 sharg_append(a, pid);
1384 sharg_append(a, xwin);
1385 sharg_append(a, uzbl.comm.fifo_path);
1386 sharg_append(a, uzbl.comm.socket_path);
1387 sharg_append(a, uzbl.state.uri);
1388 sharg_append(a, uzbl.gui.main_title);
1390 for (i = npre; i < g_strv_length((gchar**)args); i++)
1391 sharg_append(a, args[i]);
1395 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1397 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1398 NULL, NULL, output_stdout, NULL, NULL, &err);
1399 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1400 NULL, NULL, NULL, &err);
1402 if (uzbl.state.verbose) {
1403 GString *s = g_string_new("spawned:");
1404 for (i = 0; i < (a->len); i++) {
1405 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1406 g_string_append_printf(s, " %s", qarg);
1409 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1410 printf("%s\n", s->str);
1411 g_string_free(s, TRUE);
1413 printf("Stdout: %s\n", *output_stdout);
1417 g_printerr("error on run_command: %s\n", err->message);
1422 g_array_free (a, TRUE);
1427 split_quoted(const gchar* src, const gboolean unquote) {
1428 /* split on unquoted space, return array of strings;
1429 remove a layer of quotes and backslashes if unquote */
1430 if (!src) return NULL;
1432 gboolean dq = FALSE;
1433 gboolean sq = FALSE;
1434 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1435 GString *s = g_string_new ("");
1439 for (p = src; *p != '\0'; p++) {
1440 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1441 else if (*p == '\\') { g_string_append_c(s, *p++);
1442 g_string_append_c(s, *p); }
1443 else if ((*p == '"') && unquote && !sq) dq = !dq;
1444 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1446 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1447 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1449 else if ((*p == ' ') && !dq && !sq) {
1450 dup = g_strdup(s->str);
1451 g_array_append_val(a, dup);
1452 g_string_truncate(s, 0);
1453 } else g_string_append_c(s, *p);
1455 dup = g_strdup(s->str);
1456 g_array_append_val(a, dup);
1457 ret = (gchar**)a->data;
1458 g_array_free (a, FALSE);
1459 g_string_free (s, TRUE);
1464 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1465 (void)web_view; (void)result;
1466 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1467 if (argv_idx(argv, 0))
1468 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1472 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1473 (void)web_view; (void)result;
1475 if (argv_idx(argv, 0))
1476 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1477 TRUE, &uzbl.comm.sync_stdout);
1481 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1482 (void)web_view; (void)result;
1483 if (!uzbl.behave.shell_cmd) {
1484 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1489 gchar *spacer = g_strdup("");
1490 g_array_insert_val(argv, 1, spacer);
1491 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1493 for (i = 1; i < g_strv_length(cmd); i++)
1494 g_array_prepend_val(argv, cmd[i]);
1496 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1502 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1503 (void)web_view; (void)result;
1504 if (!uzbl.behave.shell_cmd) {
1505 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1510 gchar *spacer = g_strdup("");
1511 g_array_insert_val(argv, 1, spacer);
1512 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1514 for (i = 1; i < g_strv_length(cmd); i++)
1515 g_array_prepend_val(argv, cmd[i]);
1517 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1518 TRUE, &uzbl.comm.sync_stdout);
1524 parse_command(const char *cmd, const char *param, GString *result) {
1527 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1529 gchar **par = split_quoted(param, TRUE);
1530 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1532 if (c->no_split) { /* don't split */
1533 sharg_append(a, param);
1535 for (i = 0; i < g_strv_length(par); i++)
1536 sharg_append(a, par[i]);
1539 if (result == NULL) {
1540 GString *result_print = g_string_new("");
1542 c->function(uzbl.gui.web_view, a, result_print);
1543 if (result_print->len)
1544 printf("%*s\n", result_print->len, result_print->str);
1546 g_string_free(result_print, TRUE);
1548 c->function(uzbl.gui.web_view, a, result);
1551 g_array_free (a, TRUE);
1554 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1561 if(*uzbl.net.proxy_url == ' '
1562 || uzbl.net.proxy_url == NULL) {
1563 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1564 (GType) SOUP_SESSION_PROXY_URI);
1567 suri = soup_uri_new(uzbl.net.proxy_url);
1568 g_object_set(G_OBJECT(uzbl.net.soup_session),
1569 SOUP_SESSION_PROXY_URI,
1571 soup_uri_free(suri);
1578 if(file_exists(uzbl.gui.icon)) {
1579 if (uzbl.gui.main_window)
1580 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1582 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1584 g_free (uzbl.gui.icon);
1589 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1590 g_array_append_val (a, uzbl.state.uri);
1591 load_uri(uzbl.gui.web_view, a, NULL);
1592 g_array_free (a, TRUE);
1596 cmd_always_insert_mode() {
1597 uzbl.behave.insert_mode =
1598 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1604 g_object_set(G_OBJECT(uzbl.net.soup_session),
1605 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1609 cmd_max_conns_host() {
1610 g_object_set(G_OBJECT(uzbl.net.soup_session),
1611 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1616 soup_session_remove_feature
1617 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1618 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1619 /*g_free(uzbl.net.soup_logger);*/
1621 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1622 soup_session_add_feature(uzbl.net.soup_session,
1623 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1626 static WebKitWebSettings*
1628 return webkit_web_view_get_settings(uzbl.gui.web_view);
1633 WebKitWebSettings *ws = view_settings();
1634 if (uzbl.behave.font_size > 0) {
1635 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1638 if (uzbl.behave.monospace_size > 0) {
1639 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1640 uzbl.behave.monospace_size, NULL);
1642 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1643 uzbl.behave.font_size, NULL);
1649 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1653 cmd_disable_plugins() {
1654 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1655 !uzbl.behave.disable_plugins, NULL);
1659 cmd_disable_scripts() {
1660 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1661 !uzbl.behave.disable_scripts, NULL);
1665 cmd_minimum_font_size() {
1666 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1667 uzbl.behave.minimum_font_size, NULL);
1670 cmd_autoload_img() {
1671 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1672 uzbl.behave.autoload_img, NULL);
1677 cmd_autoshrink_img() {
1678 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1679 uzbl.behave.autoshrink_img, NULL);
1684 cmd_enable_spellcheck() {
1685 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1686 uzbl.behave.enable_spellcheck, NULL);
1690 cmd_enable_private() {
1691 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1692 uzbl.behave.enable_private, NULL);
1697 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1698 uzbl.behave.print_bg, NULL);
1703 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1704 uzbl.behave.style_uri, NULL);
1708 cmd_resizable_txt() {
1709 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1710 uzbl.behave.resizable_txt, NULL);
1714 cmd_default_encoding() {
1715 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1716 uzbl.behave.default_encoding, NULL);
1720 cmd_enforce_96dpi() {
1721 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1722 uzbl.behave.enforce_96dpi, NULL);
1726 cmd_caret_browsing() {
1727 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1728 uzbl.behave.caret_browsing, NULL);
1732 cmd_cookie_handler() {
1733 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1734 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1735 if ((g_strcmp0(split[0], "sh") == 0) ||
1736 (g_strcmp0(split[0], "spawn") == 0)) {
1737 g_free (uzbl.behave.cookie_handler);
1738 uzbl.behave.cookie_handler =
1739 g_strdup_printf("sync_%s %s", split[0], split[1]);
1746 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1751 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1756 if(uzbl.behave.inject_html) {
1757 webkit_web_view_load_html_string (uzbl.gui.web_view,
1758 uzbl.behave.inject_html, NULL);
1767 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1768 uzbl.behave.modmask = 0;
1770 if(uzbl.behave.modkey)
1771 g_free(uzbl.behave.modkey);
1772 uzbl.behave.modkey = buf;
1774 for (i = 0; modkeys[i].key != NULL; i++) {
1775 if (g_strrstr(buf, modkeys[i].key))
1776 uzbl.behave.modmask |= modkeys[i].mask;
1782 if (*uzbl.net.useragent == ' ') {
1783 g_free (uzbl.net.useragent);
1784 uzbl.net.useragent = NULL;
1786 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1787 uzbl.net.useragent, NULL);
1793 gtk_widget_ref(uzbl.gui.scrolled_win);
1794 gtk_widget_ref(uzbl.gui.mainbar);
1795 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1796 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1798 if(uzbl.behave.status_top) {
1799 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1800 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1803 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1804 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1806 gtk_widget_unref(uzbl.gui.scrolled_win);
1807 gtk_widget_unref(uzbl.gui.mainbar);
1808 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1813 set_var_value(gchar *name, gchar *val) {
1814 uzbl_cmdprop *c = NULL;
1818 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1819 /* check for the variable type */
1820 if (c->type == TYPE_STR) {
1821 buf = expand(val, 0, FALSE);
1824 } else if(c->type == TYPE_INT) {
1825 int *ip = (int *)c->ptr;
1826 buf = expand(val, 0, FALSE);
1827 *ip = (int)strtoul(buf, &endp, 10);
1829 } else if (c->type == TYPE_FLOAT) {
1830 float *fp = (float *)c->ptr;
1831 buf = expand(val, 0, FALSE);
1832 *fp = strtod(buf, &endp);
1836 /* invoke a command specific function */
1837 if(c->func) c->func();
1844 Behaviour *b = &uzbl.behave;
1846 if(b->html_buffer->str) {
1847 webkit_web_view_load_html_string (uzbl.gui.web_view,
1848 b->html_buffer->str, b->base_url);
1849 g_string_free(b->html_buffer, TRUE);
1850 b->html_buffer = g_string_new("");
1854 enum {M_CMD, M_HTML};
1856 parse_cmd_line(const char *ctl_line, GString *result) {
1857 Behaviour *b = &uzbl.behave;
1860 if(b->mode == M_HTML) {
1861 len = strlen(b->html_endmarker);
1862 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1863 if(len == strlen(ctl_line)-1 &&
1864 !strncmp(b->html_endmarker, ctl_line, len)) {
1866 set_var_value("mode", "0");
1871 set_timeout(b->html_timeout);
1872 g_string_append(b->html_buffer, ctl_line);
1875 else if((ctl_line[0] == '#') /* Comments */
1876 || (ctl_line[0] == ' ')
1877 || (ctl_line[0] == '\n'))
1878 ; /* ignore these lines */
1879 else { /* parse a command */
1881 gchar **tokens = NULL;
1882 len = strlen(ctl_line);
1884 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1885 ctlstrip = g_strndup(ctl_line, len - 1);
1886 else ctlstrip = g_strdup(ctl_line);
1888 tokens = g_strsplit(ctlstrip, " ", 2);
1889 parse_command(tokens[0], tokens[1], result);
1896 build_stream_name(int type, const gchar* dir) {
1897 char *xwin_str = NULL;
1898 State *s = &uzbl.state;
1901 xwin_str = itos((int)uzbl.xwin);
1903 str = g_strdup_printf
1904 ("%s/uzbl_fifo_%s", dir,
1905 s->instance_name ? s->instance_name : xwin_str);
1906 } else if (type == SOCKET) {
1907 str = g_strdup_printf
1908 ("%s/uzbl_socket_%s", dir,
1909 s->instance_name ? s->instance_name : xwin_str );
1916 control_fifo(GIOChannel *gio, GIOCondition condition) {
1917 if (uzbl.state.verbose)
1918 printf("triggered\n");
1923 if (condition & G_IO_HUP)
1924 g_error ("Fifo: Read end of pipe died!\n");
1927 g_error ("Fifo: GIOChannel broke\n");
1929 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1930 if (ret == G_IO_STATUS_ERROR) {
1931 g_error ("Fifo: Error reading: %s\n", err->message);
1935 parse_cmd_line(ctl_line, NULL);
1942 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1943 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1944 if (unlink(uzbl.comm.fifo_path) == -1)
1945 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1946 g_free(uzbl.comm.fifo_path);
1947 uzbl.comm.fifo_path = NULL;
1950 if (*dir == ' ') { /* space unsets the variable */
1955 GIOChannel *chan = NULL;
1956 GError *error = NULL;
1957 gchar *path = build_stream_name(FIFO, dir);
1959 if (!file_exists(path)) {
1960 if (mkfifo (path, 0666) == 0) {
1961 // we don't really need to write to the file, but if we open the file as 'r' we will block here, waiting for a writer to open the file.
1962 chan = g_io_channel_new_file(path, "r+", &error);
1964 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1965 if (uzbl.state.verbose)
1966 printf ("init_fifo: created successfully as %s\n", path);
1967 uzbl.comm.fifo_path = path;
1969 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1970 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1971 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1972 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1974 /* if we got this far, there was an error; cleanup */
1975 if (error) g_error_free (error);
1982 control_stdin(GIOChannel *gio, GIOCondition condition) {
1984 gchar *ctl_line = NULL;
1987 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1988 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1991 parse_cmd_line(ctl_line, NULL);
1999 GIOChannel *chan = NULL;
2000 GError *error = NULL;
2002 chan = g_io_channel_unix_new(fileno(stdin));
2004 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2005 g_error ("Stdin: could not add watch\n");
2007 if (uzbl.state.verbose)
2008 printf ("Stdin: watch added successfully\n");
2011 g_error ("Stdin: Error while opening: %s\n", error->message);
2013 if (error) g_error_free (error);
2017 control_socket(GIOChannel *chan) {
2018 struct sockaddr_un remote;
2019 unsigned int t = sizeof(remote);
2021 GIOChannel *clientchan;
2023 clientsock = accept (g_io_channel_unix_get_fd(chan),
2024 (struct sockaddr *) &remote, &t);
2026 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2027 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2028 (GIOFunc) control_client_socket, clientchan);
2035 control_client_socket(GIOChannel *clientchan) {
2037 GString *result = g_string_new("");
2038 GError *error = NULL;
2042 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2043 if (ret == G_IO_STATUS_ERROR) {
2044 g_warning ("Error reading: %s\n", error->message);
2045 g_io_channel_shutdown(clientchan, TRUE, &error);
2047 } else if (ret == G_IO_STATUS_EOF) {
2048 /* shutdown and remove channel watch from main loop */
2049 g_io_channel_shutdown(clientchan, TRUE, &error);
2054 parse_cmd_line (ctl_line, result);
2055 g_string_append_c(result, '\n');
2056 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2058 if (ret == G_IO_STATUS_ERROR) {
2059 g_warning ("Error writing: %s", error->message);
2061 g_io_channel_flush(clientchan, &error);
2064 if (error) g_error_free (error);
2065 g_string_free(result, TRUE);
2071 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2072 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2073 if (unlink(uzbl.comm.socket_path) == -1)
2074 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2075 g_free(uzbl.comm.socket_path);
2076 uzbl.comm.socket_path = NULL;
2084 GIOChannel *chan = NULL;
2086 struct sockaddr_un local;
2087 gchar *path = build_stream_name(SOCKET, dir);
2089 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2091 local.sun_family = AF_UNIX;
2092 strcpy (local.sun_path, path);
2093 unlink (local.sun_path);
2095 len = strlen (local.sun_path) + sizeof (local.sun_family);
2096 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2097 if (uzbl.state.verbose)
2098 printf ("init_socket: opened in %s\n", path);
2101 if( (chan = g_io_channel_unix_new(sock)) ) {
2102 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2103 uzbl.comm.socket_path = path;
2106 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2108 /* if we got this far, there was an error; cleanup */
2115 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2116 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2118 // this function may be called very early when the templates are not set (yet), hence the checks
2120 update_title (void) {
2121 Behaviour *b = &uzbl.behave;
2124 if (b->show_status) {
2125 if (b->title_format_short) {
2126 parsed = expand_template(b->title_format_short, FALSE);
2127 if (uzbl.gui.main_window)
2128 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2131 if (b->status_format) {
2132 parsed = expand_template(b->status_format, TRUE);
2133 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2136 if (b->status_background) {
2138 gdk_color_parse (b->status_background, &color);
2139 //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2140 if (uzbl.gui.main_window)
2141 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2142 else if (uzbl.gui.plug)
2143 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2146 if (b->title_format_long) {
2147 parsed = expand_template(b->title_format_long, FALSE);
2148 if (uzbl.gui.main_window)
2149 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2156 key_press_cb (GtkWidget* window, GdkEventKey* event)
2158 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2162 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2163 || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
2166 /* turn off insert mode (if always_insert_mode is not used) */
2167 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2168 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2173 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2176 if (event->keyval == GDK_Escape) {
2177 g_string_truncate(uzbl.state.keycmd, 0);
2179 dehilight(uzbl.gui.web_view, NULL, NULL);
2183 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2184 if (event->keyval == GDK_Insert) {
2186 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2187 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2189 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2192 g_string_append (uzbl.state.keycmd, str);
2199 if (event->keyval == GDK_BackSpace)
2200 keycmd_bs(NULL, NULL, NULL);
2202 gboolean key_ret = FALSE;
2203 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2205 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2207 run_keycmd(key_ret);
2209 if (key_ret) return (!uzbl.behave.insert_mode);
2214 run_keycmd(const gboolean key_ret) {
2215 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2217 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2218 g_string_truncate(uzbl.state.keycmd, 0);
2219 parse_command(act->name, act->param, NULL);
2223 /* try if it's an incremental keycmd or one that takes args, and run it */
2224 GString* short_keys = g_string_new ("");
2225 GString* short_keys_inc = g_string_new ("");
2227 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2228 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2229 g_string_assign(short_keys_inc, short_keys->str);
2230 g_string_append_c(short_keys, '_');
2231 g_string_append_c(short_keys_inc, '*');
2233 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2234 /* run normal cmds only if return was pressed */
2235 exec_paramcmd(act, i);
2236 g_string_truncate(uzbl.state.keycmd, 0);
2238 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2239 if (key_ret) /* just quit the incremental command on return */
2240 g_string_truncate(uzbl.state.keycmd, 0);
2241 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2245 g_string_truncate(short_keys, short_keys->len - 1);
2247 g_string_free (short_keys, TRUE);
2248 g_string_free (short_keys_inc, TRUE);
2252 exec_paramcmd(const Action *act, const guint i) {
2253 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2254 GString *actionname = g_string_new ("");
2255 GString *actionparam = g_string_new ("");
2256 g_string_erase (parampart, 0, i+1);
2258 g_string_printf (actionname, act->name, parampart->str);
2260 g_string_printf (actionparam, act->param, parampart->str);
2261 parse_command(actionname->str, actionparam->str, NULL);
2262 g_string_free(actionname, TRUE);
2263 g_string_free(actionparam, TRUE);
2264 g_string_free(parampart, TRUE);
2272 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2273 //main_window_ref = g_object_ref(scrolled_window);
2274 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2276 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2277 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2279 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2280 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2281 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2282 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2283 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2284 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2285 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2286 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2287 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2288 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2289 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2291 return scrolled_window;
2298 g->mainbar = gtk_hbox_new (FALSE, 0);
2300 /* keep a reference to the bar so we can re-pack it at runtime*/
2301 //sbar_ref = g_object_ref(g->mainbar);
2303 g->mainbar_label = gtk_label_new ("");
2304 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2305 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2306 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2307 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2308 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2309 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2314 GtkWidget* create_window () {
2315 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2316 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2317 gtk_widget_set_name (window, "Uzbl browser");
2318 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2319 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2325 GtkPlug* create_plug () {
2326 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2327 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2328 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2335 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2337 If actname is one that calls an external command, this function will inject
2338 newargs in front of the user-provided args in that command line. They will
2339 come become after the body of the script (in sh) or after the name of
2340 the command to execute (in spawn).
2341 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2342 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2344 The return value consist of two strings: the action (sh, ...) and its args.
2346 If act is not one that calls an external command, then the given action merely
2349 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2350 gchar *actdup = g_strdup(actname);
2351 g_array_append_val(rets, actdup);
2353 if ((g_strcmp0(actname, "spawn") == 0) ||
2354 (g_strcmp0(actname, "sh") == 0) ||
2355 (g_strcmp0(actname, "sync_spawn") == 0) ||
2356 (g_strcmp0(actname, "sync_sh") == 0)) {
2358 GString *a = g_string_new("");
2359 gchar **spawnparts = split_quoted(origargs, FALSE);
2360 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2361 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2363 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2364 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2366 g_array_append_val(rets, a->str);
2367 g_string_free(a, FALSE);
2368 g_strfreev(spawnparts);
2370 gchar *origdup = g_strdup(origargs);
2371 g_array_append_val(rets, origdup);
2373 return (gchar**)g_array_free(rets, FALSE);
2377 run_handler (const gchar *act, const gchar *args) {
2378 /* Consider this code a temporary hack to make the handlers usable.
2379 In practice, all this splicing, injection, and reconstruction is
2380 inefficient, annoying and hard to manage. Potential pitfalls arise
2381 when the handler specific args 1) are not quoted (the handler
2382 callbacks should take care of this) 2) are quoted but interfere
2383 with the users' own quotation. A more ideal solution is
2384 to refactor parse_command so that it doesn't just take a string
2385 and execute it; rather than that, we should have a function which
2386 returns the argument vector parsed from the string. This vector
2387 could be modified (e.g. insert additional args into it) before
2388 passing it to the next function that actually executes it. Though
2389 it still isn't perfect for chain actions.. will reconsider & re-
2390 factor when I have the time. -duc */
2392 char **parts = g_strsplit(act, " ", 2);
2394 if (g_strcmp0(parts[0], "chain") == 0) {
2395 GString *newargs = g_string_new("");
2396 gchar **chainparts = split_quoted(parts[1], FALSE);
2398 /* for every argument in the chain, inject the handler args
2399 and make sure the new parts are wrapped in quotes */
2400 gchar **cp = chainparts;
2402 gchar *quotless = NULL;
2403 gchar **spliced_quotless = NULL; // sigh -_-;
2404 gchar **inpart = NULL;
2407 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2409 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2410 } else quotless = g_strdup(*cp);
2412 spliced_quotless = g_strsplit(quotless, " ", 2);
2413 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2414 g_strfreev(spliced_quotless);
2416 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2422 parse_command(parts[0], &(newargs->str[1]), NULL);
2423 g_string_free(newargs, TRUE);
2424 g_strfreev(chainparts);
2427 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2428 parse_command(inparts[0], inparts[1], NULL);
2436 add_binding (const gchar *key, const gchar *act) {
2437 char **parts = g_strsplit(act, " ", 2);
2444 if (uzbl.state.verbose)
2445 printf ("Binding %-10s : %s\n", key, act);
2446 action = new_action(parts[0], parts[1]);
2448 if (g_hash_table_remove (uzbl.bindings, key))
2449 g_warning ("Overwriting existing binding for \"%s\"", key);
2450 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2455 get_xdg_var (XDG_Var xdg) {
2456 const gchar* actual_value = getenv (xdg.environmental);
2457 const gchar* home = getenv ("HOME");
2458 gchar* return_value;
2460 if (! actual_value || strcmp (actual_value, "") == 0) {
2461 if (xdg.default_value) {
2462 return_value = str_replace ("~", home, xdg.default_value);
2464 return_value = NULL;
2467 return_value = str_replace("~", home, actual_value);
2470 return return_value;
2474 find_xdg_file (int xdg_type, char* filename) {
2475 /* xdg_type = 0 => config
2476 xdg_type = 1 => data
2477 xdg_type = 2 => cache*/
2479 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2480 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2483 gchar* temporary_string;
2487 if (! file_exists (temporary_file) && xdg_type != 2) {
2488 buf = get_xdg_var (XDG[3 + xdg_type]);
2489 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2492 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2493 g_free (temporary_file);
2494 temporary_file = g_strconcat (temporary_string, filename, NULL);
2498 //g_free (temporary_string); - segfaults.
2500 if (file_exists (temporary_file)) {
2501 return temporary_file;
2508 State *s = &uzbl.state;
2509 Network *n = &uzbl.net;
2511 for (i = 0; default_config[i].command != NULL; i++) {
2512 parse_cmd_line(default_config[i].command, NULL);
2515 if (g_strcmp0(s->config_file, "-") == 0) {
2516 s->config_file = NULL;
2520 else if (!s->config_file) {
2521 s->config_file = find_xdg_file (0, "/uzbl/config");
2524 if (s->config_file) {
2525 GArray* lines = read_file_by_line (s->config_file);
2529 while ((line = g_array_index(lines, gchar*, i))) {
2530 parse_cmd_line (line, NULL);
2534 g_array_free (lines, TRUE);
2536 if (uzbl.state.verbose)
2537 printf ("No configuration file loaded.\n");
2540 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2543 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2546 if (!uzbl.behave.cookie_handler)
2549 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2550 GString *s = g_string_new ("");
2551 SoupURI * soup_uri = soup_message_get_uri(msg);
2552 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2553 run_handler(uzbl.behave.cookie_handler, s->str);
2555 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2556 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2557 if ( p != NULL ) *p = '\0';
2558 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2560 if (uzbl.comm.sync_stdout)
2561 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2563 g_string_free(s, TRUE);
2567 save_cookies (SoupMessage *msg, gpointer user_data){
2571 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2572 cookie = soup_cookie_to_set_cookie_header(ck->data);
2573 SoupURI * soup_uri = soup_message_get_uri(msg);
2574 GString *s = g_string_new ("");
2575 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2576 run_handler(uzbl.behave.cookie_handler, s->str);
2578 g_string_free(s, TRUE);
2583 /* --- WEBINSPECTOR --- */
2585 hide_window_cb(GtkWidget *widget, gpointer data) {
2588 gtk_widget_hide(widget);
2591 static WebKitWebView*
2592 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2595 (void) web_inspector;
2596 GtkWidget* scrolled_window;
2597 GtkWidget* new_web_view;
2600 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2601 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2602 G_CALLBACK(hide_window_cb), NULL);
2604 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2605 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2606 gtk_widget_show(g->inspector_window);
2608 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2609 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2610 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2611 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2612 gtk_widget_show(scrolled_window);
2614 new_web_view = webkit_web_view_new();
2615 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2617 return WEBKIT_WEB_VIEW(new_web_view);
2621 inspector_show_window_cb (WebKitWebInspector* inspector){
2623 gtk_widget_show(uzbl.gui.inspector_window);
2627 /* TODO: Add variables and code to make use of these functions */
2629 inspector_close_window_cb (WebKitWebInspector* inspector){
2635 inspector_attach_window_cb (WebKitWebInspector* inspector){
2641 inspector_detach_window_cb (WebKitWebInspector* inspector){
2647 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2653 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2659 set_up_inspector() {
2661 WebKitWebSettings *settings = view_settings();
2662 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2664 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2665 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2666 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2667 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2668 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2669 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2670 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2672 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2676 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2678 uzbl_cmdprop *c = v;
2683 if(c->type == TYPE_STR)
2684 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2685 else if(c->type == TYPE_INT)
2686 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2690 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2694 printf("bind %s = %s %s\n", (char *)k ,
2695 (char *)a->name, a->param?(char *)a->param:"");
2700 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2701 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2704 #ifndef UZBL_LIBRARY
2707 main (int argc, char* argv[]) {
2708 gtk_init (&argc, &argv);
2709 if (!g_thread_supported ())
2710 g_thread_init (NULL);
2711 uzbl.state.executable_path = g_strdup(argv[0]);
2712 uzbl.state.selected_url = NULL;
2713 uzbl.state.searchtx = NULL;
2715 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2716 g_option_context_add_main_entries (context, entries, NULL);
2717 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2718 g_option_context_parse (context, &argc, &argv, NULL);
2719 g_option_context_free(context);
2721 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2722 gboolean verbose_override = uzbl.state.verbose;
2724 /* initialize hash table */
2725 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2727 uzbl.net.soup_session = webkit_get_default_session();
2728 uzbl.state.keycmd = g_string_new("");
2730 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2731 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2732 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2733 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2734 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2735 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2738 if(uname(&uzbl.state.unameinfo) == -1)
2739 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2741 uzbl.gui.sbar.progress_s = g_strdup("=");
2742 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2743 uzbl.gui.sbar.progress_w = 10;
2745 /* HTML mode defaults*/
2746 uzbl.behave.html_buffer = g_string_new("");
2747 uzbl.behave.html_endmarker = g_strdup(".");
2748 uzbl.behave.html_timeout = 60;
2749 uzbl.behave.base_url = g_strdup("http://invalid");
2751 /* default mode indicators */
2752 uzbl.behave.insert_indicator = g_strdup("I");
2753 uzbl.behave.cmd_indicator = g_strdup("C");
2757 make_var_to_name_hash();
2759 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2761 uzbl.gui.scrolled_win = create_browser();
2764 /* initial packing */
2765 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2766 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2768 if (uzbl.state.socket_id) {
2769 uzbl.gui.plug = create_plug ();
2770 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2771 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2773 uzbl.gui.main_window = create_window ();
2774 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2775 gtk_widget_show_all (uzbl.gui.main_window);
2776 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2779 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2781 if (uzbl.state.verbose) {
2782 printf("Uzbl start location: %s\n", argv[0]);
2783 if (uzbl.state.socket_id)
2784 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2786 printf("window_id %i\n",(int) uzbl.xwin);
2787 printf("pid %i\n", getpid ());
2788 printf("name: %s\n", uzbl.state.instance_name);
2791 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2792 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2793 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2794 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2795 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2799 if (!uzbl.behave.show_status)
2800 gtk_widget_hide(uzbl.gui.mainbar);
2807 if (verbose_override > uzbl.state.verbose)
2808 uzbl.state.verbose = verbose_override;
2811 set_var_value("uri", uri_override);
2812 g_free(uri_override);
2813 } else if (uzbl.state.uri)
2814 cmd_load_uri(uzbl.gui.web_view, NULL);
2819 return EXIT_SUCCESS;
2823 /* vi: set et ts=4: */