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)},
164 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
165 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
166 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
167 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
168 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
169 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
171 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
172 }, *n2v_p = var_name_to_ptr;
179 { "SHIFT", GDK_SHIFT_MASK }, // shift
180 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
181 { "CONTROL", GDK_CONTROL_MASK }, // control
182 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
183 { "MOD2", GDK_MOD2_MASK }, // 5th mod
184 { "MOD3", GDK_MOD3_MASK }, // 6th mod
185 { "MOD4", GDK_MOD4_MASK }, // 7th mod
186 { "MOD5", GDK_MOD5_MASK }, // 8th mod
187 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
188 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
189 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
190 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
191 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
192 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
193 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
194 { "META", GDK_META_MASK }, // meta (since 2.10)
199 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
200 * for quick access */
202 make_var_to_name_hash() {
203 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
205 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
210 /* --- UTILITY FUNCTIONS --- */
212 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
214 get_exp_type(gchar *s) {
218 else if(*(s+1) == '{')
219 return EXP_BRACED_VAR;
220 else if(*(s+1) == '<')
223 return EXP_SIMPLE_VAR;
229 * recurse == 1: don't expand '@(command)@'
230 * recurse == 2: don't expand '@<java script>@'
233 expand(char *s, guint recurse, gboolean escape_markup) {
237 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
242 gchar *cmd_stdout = NULL;
244 GString *buf = g_string_new("");
245 GString *js_ret = g_string_new("");
250 g_string_append_c(buf, *++s);
255 etype = get_exp_type(s);
260 if( (vend = strpbrk(s, end_simple_var)) ||
261 (vend = strchr(s, '\0')) ) {
262 strncpy(ret, s, vend-s);
268 if( (vend = strchr(s, upto)) ||
269 (vend = strchr(s, '\0')) ) {
270 strncpy(ret, s, vend-s);
276 strcpy(str_end, ")@");
278 if( (vend = strstr(s, str_end)) ||
279 (vend = strchr(s, '\0')) ) {
280 strncpy(ret, s, vend-s);
286 strcpy(str_end, ">@");
288 if( (vend = strstr(s, str_end)) ||
289 (vend = strchr(s, '\0')) ) {
290 strncpy(ret, s, vend-s);
296 if(etype == EXP_SIMPLE_VAR ||
297 etype == EXP_BRACED_VAR) {
298 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
299 if(c->type == TYPE_STR && *c->ptr != NULL) {
301 char *b = g_markup_escape_text((gchar *)*c->ptr,
302 strlen((gchar *)*c->ptr));
303 g_string_append(buf, b);
306 g_string_append(buf, (gchar *)*c->ptr);
308 } else if(c->type == TYPE_INT) {
309 char *b = itos((uintptr_t)*c->ptr);
310 g_string_append(buf, b);
315 if(etype == EXP_SIMPLE_VAR)
320 else if(recurse != 1 &&
322 mycmd = expand(ret, 1, escape_markup);
323 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
327 g_printerr("error on running command: %s\n", err->message);
330 else if (*cmd_stdout) {
331 int len = strlen(cmd_stdout);
333 if(cmd_stdout[len-1] == '\n')
334 cmd_stdout[--len] = 0; /* strip trailing newline */
337 char *b = g_markup_escape_text(cmd_stdout, len);
338 g_string_append(buf, b);
341 g_string_append(buf, cmd_stdout);
347 else if(recurse != 2 &&
349 mycmd = expand(ret, 2, escape_markup);
350 eval_js(uzbl.gui.web_view, mycmd, js_ret);
355 char *b = g_markup_escape_text(js_ret->str,
356 strlen(js_ret->str));
357 g_string_append(buf, b);
360 g_string_append(buf, js_ret->str);
362 g_string_free(js_ret, TRUE);
363 js_ret = g_string_new("");
370 g_string_append_c(buf, *s);
375 g_string_free(js_ret, TRUE);
376 return g_string_free(buf, FALSE);
383 snprintf(tmp, sizeof(tmp), "%i", val);
384 return g_strdup(tmp);
388 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
391 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
394 str_replace (const char* search, const char* replace, const char* string) {
398 buf = g_strsplit (string, search, -1);
399 ret = g_strjoinv (replace, buf);
400 g_strfreev(buf); // somebody said this segfaults
406 read_file_by_line (gchar *path) {
407 GIOChannel *chan = NULL;
408 gchar *readbuf = NULL;
410 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
413 chan = g_io_channel_new_file(path, "r", NULL);
416 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
417 const gchar* val = g_strdup (readbuf);
418 g_array_append_val (lines, val);
423 g_io_channel_unref (chan);
425 fprintf(stderr, "File '%s' not be read.\n", path);
432 gchar* parseenv (char* string) {
433 extern char** environ;
434 gchar* tmpstr = NULL;
438 while (environ[i] != NULL) {
439 gchar** env = g_strsplit (environ[i], "=", 2);
440 gchar* envname = g_strconcat ("$", env[0], NULL);
442 if (g_strrstr (string, envname) != NULL) {
443 tmpstr = g_strdup(string);
445 string = str_replace(envname, env[1], tmpstr);
450 g_strfreev (env); // somebody said this breaks uzbl
458 setup_signal(int signr, sigfunc *shandler) {
459 struct sigaction nh, oh;
461 nh.sa_handler = shandler;
462 sigemptyset(&nh.sa_mask);
465 if(sigaction(signr, &nh, &oh) < 0)
473 if (uzbl.behave.fifo_dir)
474 unlink (uzbl.comm.fifo_path);
475 if (uzbl.behave.socket_dir)
476 unlink (uzbl.comm.socket_path);
478 g_free(uzbl.state.executable_path);
479 g_string_free(uzbl.state.keycmd, TRUE);
480 g_hash_table_destroy(uzbl.bindings);
481 g_hash_table_destroy(uzbl.behave.commands);
484 /* used for html_mode_timeout
485 * be sure to extend this function to use
486 * more timers if needed in other places
489 set_timeout(int seconds) {
491 memset(&t, 0, sizeof t);
493 t.it_value.tv_sec = seconds;
494 t.it_value.tv_usec = 0;
495 setitimer(ITIMER_REAL, &t, NULL);
498 /* --- SIGNAL HANDLER --- */
501 catch_sigterm(int s) {
507 catch_sigint(int s) {
517 set_var_value("mode", "0");
522 /* --- CALLBACKS --- */
525 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
528 (void) navigation_action;
529 (void) policy_decision;
531 const gchar* uri = webkit_network_request_get_uri (request);
532 if (uzbl.state.verbose)
533 printf("New window requested -> %s \n", uri);
534 new_window_load_uri(uri);
539 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
544 /* If we can display it, let's display it... */
545 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
546 webkit_web_policy_decision_use (policy_decision);
550 /* ...everything we can't displayed is downloaded */
551 webkit_web_policy_decision_download (policy_decision);
556 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
560 if (uzbl.state.selected_url != NULL) {
561 if (uzbl.state.verbose)
562 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
563 new_window_load_uri(uzbl.state.selected_url);
565 if (uzbl.state.verbose)
566 printf("New web view -> %s\n","Nothing to open, exiting");
572 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
575 if (uzbl.behave.download_handler) {
576 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
577 if (uzbl.state.verbose)
578 printf("Download -> %s\n",uri);
579 /* if urls not escaped, we may have to escape and quote uri before this call */
580 run_handler(uzbl.behave.download_handler, uri);
585 /* scroll a bar in a given direction */
587 scroll (GtkAdjustment* bar, GArray *argv) {
591 gdouble page_size = gtk_adjustment_get_page_size(bar);
592 gdouble value = gtk_adjustment_get_value(bar);
593 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
596 value += page_size * amount * 0.01;
600 max_value = gtk_adjustment_get_upper(bar) - page_size;
602 if (value > max_value)
603 value = max_value; /* don't scroll past the end of the page */
605 gtk_adjustment_set_value (bar, value);
609 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
610 (void) page; (void) argv; (void) result;
611 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
615 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
616 (void) page; (void) argv; (void) result;
617 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
618 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
622 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
623 (void) page; (void) result;
624 scroll(uzbl.gui.bar_v, argv);
628 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
629 (void) page; (void) result;
630 scroll(uzbl.gui.bar_h, argv);
635 if (!uzbl.behave.show_status) {
636 gtk_widget_hide(uzbl.gui.mainbar);
638 gtk_widget_show(uzbl.gui.mainbar);
644 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
649 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
653 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
658 if (uzbl.behave.show_status) {
659 gtk_widget_hide(uzbl.gui.mainbar);
661 gtk_widget_show(uzbl.gui.mainbar);
663 uzbl.behave.show_status = !uzbl.behave.show_status;
668 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
672 //Set selected_url state variable
673 g_free(uzbl.state.selected_url);
674 uzbl.state.selected_url = NULL;
676 uzbl.state.selected_url = g_strdup(link);
682 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
685 const gchar *title = webkit_web_view_get_title(web_view);
686 if (uzbl.gui.main_title)
687 g_free (uzbl.gui.main_title);
688 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
693 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
696 uzbl.gui.sbar.load_progress = progress;
698 g_free(uzbl.gui.sbar.progress_bar);
699 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
705 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
709 if (uzbl.behave.load_finish_handler)
710 run_handler(uzbl.behave.load_finish_handler, "");
714 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
718 uzbl.gui.sbar.load_progress = 0;
719 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
720 if (uzbl.behave.load_start_handler)
721 run_handler(uzbl.behave.load_start_handler, "");
725 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
728 g_free (uzbl.state.uri);
729 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
730 uzbl.state.uri = g_string_free (newuri, FALSE);
731 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
732 set_insert_mode(uzbl.behave.always_insert_mode);
735 if (uzbl.behave.load_commit_handler)
736 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
740 destroy_cb (GtkWidget* widget, gpointer data) {
748 if (uzbl.behave.history_handler) {
750 struct tm * timeinfo;
753 timeinfo = localtime ( &rawtime );
754 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
755 run_handler(uzbl.behave.history_handler, date);
760 /* VIEW funcs (little webkit wrappers) */
761 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
763 VIEWFUNC(reload_bypass_cache)
764 VIEWFUNC(stop_loading)
771 /* -- command to callback/function map for things we cannot attach to any signals */
772 static struct {char *key; CommandInfo value;} cmdlist[] =
773 { /* key function no_split */
774 { "back", {view_go_back, 0} },
775 { "forward", {view_go_forward, 0} },
776 { "scroll_vert", {scroll_vert, 0} },
777 { "scroll_horz", {scroll_horz, 0} },
778 { "scroll_begin", {scroll_begin, 0} },
779 { "scroll_end", {scroll_end, 0} },
780 { "reload", {view_reload, 0}, },
781 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
782 { "stop", {view_stop_loading, 0}, },
783 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
784 { "zoom_out", {view_zoom_out, 0}, },
785 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
786 { "uri", {load_uri, TRUE} },
787 { "js", {run_js, TRUE} },
788 { "script", {run_external_js, 0} },
789 { "toggle_status", {toggle_status_cb, 0} },
790 { "spawn", {spawn, 0} },
791 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
792 { "sh", {spawn_sh, 0} },
793 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
794 { "exit", {close_uzbl, 0} },
795 { "search", {search_forward_text, TRUE} },
796 { "search_reverse", {search_reverse_text, TRUE} },
797 { "dehilight", {dehilight, 0} },
798 { "toggle_insert_mode", {toggle_insert_mode, 0} },
799 { "set", {set_var, TRUE} },
800 //{ "get", {get_var, TRUE} },
801 { "bind", {act_bind, TRUE} },
802 { "dump_config", {act_dump_config, 0} },
803 { "keycmd", {keycmd, TRUE} },
804 { "keycmd_nl", {keycmd_nl, TRUE} },
805 { "keycmd_bs", {keycmd_bs, 0} },
806 { "chain", {chain, 0} },
807 { "print", {print, TRUE} }
814 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
816 for (i = 0; i < LENGTH(cmdlist); i++)
817 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
820 /* -- CORE FUNCTIONS -- */
823 free_action(gpointer act) {
824 Action *action = (Action*)act;
825 g_free(action->name);
827 g_free(action->param);
832 new_action(const gchar *name, const gchar *param) {
833 Action *action = g_new(Action, 1);
835 action->name = g_strdup(name);
837 action->param = g_strdup(param);
839 action->param = NULL;
845 file_exists (const char * filename) {
846 return (access(filename, F_OK) == 0);
850 set_var(WebKitWebView *page, GArray *argv, GString *result) {
851 (void) page; (void) result;
852 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
853 if (split[0] != NULL) {
854 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
855 set_var_value(g_strstrip(split[0]), value);
862 print(WebKitWebView *page, GArray *argv, GString *result) {
863 (void) page; (void) result;
866 buf = expand(argv_idx(argv, 0), 0, FALSE);
867 g_string_assign(result, buf);
872 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
873 (void) page; (void) result;
874 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
875 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
876 add_binding(g_strstrip(split[0]), value);
887 void set_insert_mode(gboolean mode) {
888 uzbl.behave.insert_mode = mode;
889 uzbl.gui.sbar.mode_indicator = (mode ?
890 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
894 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
895 (void) page; (void) result;
897 if (argv_idx(argv, 0)) {
898 if (strcmp (argv_idx(argv, 0), "0") == 0) {
899 set_insert_mode(FALSE);
901 set_insert_mode(TRUE);
904 set_insert_mode( !uzbl.behave.insert_mode );
911 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
914 if (argv_idx(argv, 0)) {
915 GString* newuri = g_string_new (argv_idx(argv, 0));
916 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
917 run_js(web_view, argv, NULL);
920 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
921 g_string_prepend (newuri, "http://");
922 /* if we do handle cookies, ask our handler for them */
923 webkit_web_view_load_uri (web_view, newuri->str);
924 g_string_free (newuri, TRUE);
932 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
933 size_t argumentCount, const JSValueRef arguments[],
934 JSValueRef* exception) {
939 JSStringRef js_result_string;
940 GString *result = g_string_new("");
942 if (argumentCount >= 1) {
943 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
944 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
945 char ctl_line[arg_size];
946 JSStringGetUTF8CString(arg, ctl_line, arg_size);
948 parse_cmd_line(ctl_line, result);
950 JSStringRelease(arg);
952 js_result_string = JSStringCreateWithUTF8CString(result->str);
954 g_string_free(result, TRUE);
956 return JSValueMakeString(ctx, js_result_string);
959 static JSStaticFunction js_static_functions[] = {
960 {"run", js_run_command, kJSPropertyAttributeNone},
965 /* This function creates the class and its definition, only once */
966 if (!uzbl.js.initialized) {
967 /* it would be pretty cool to make this dynamic */
968 uzbl.js.classdef = kJSClassDefinitionEmpty;
969 uzbl.js.classdef.staticFunctions = js_static_functions;
971 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
977 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
978 WebKitWebFrame *frame;
979 JSGlobalContextRef context;
980 JSObjectRef globalobject;
981 JSStringRef var_name;
983 JSStringRef js_script;
984 JSValueRef js_result;
985 JSStringRef js_result_string;
986 size_t js_result_size;
990 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
991 context = webkit_web_frame_get_global_context(frame);
992 globalobject = JSContextGetGlobalObject(context);
994 /* uzbl javascript namespace */
995 var_name = JSStringCreateWithUTF8CString("Uzbl");
996 JSObjectSetProperty(context, globalobject, var_name,
997 JSObjectMake(context, uzbl.js.classref, NULL),
998 kJSClassAttributeNone, NULL);
1000 /* evaluate the script and get return value*/
1001 js_script = JSStringCreateWithUTF8CString(script);
1002 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1003 if (js_result && !JSValueIsUndefined(context, js_result)) {
1004 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1005 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1007 if (js_result_size) {
1008 char js_result_utf8[js_result_size];
1009 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1010 g_string_assign(result, js_result_utf8);
1013 JSStringRelease(js_result_string);
1017 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1019 JSStringRelease(var_name);
1020 JSStringRelease(js_script);
1024 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1026 if (argv_idx(argv, 0))
1027 eval_js(web_view, argv_idx(argv, 0), result);
1031 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1033 if (argv_idx(argv, 0)) {
1034 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1039 while ((line = g_array_index(lines, gchar*, i))) {
1041 js = g_strdup (line);
1043 gchar* newjs = g_strconcat (js, line, NULL);
1050 if (uzbl.state.verbose)
1051 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1053 if (argv_idx (argv, 1)) {
1054 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1058 eval_js (web_view, js, result);
1060 g_array_free (lines, TRUE);
1065 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1066 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1067 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1068 webkit_web_view_unmark_text_matches (page);
1069 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1070 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1074 if (uzbl.state.searchtx) {
1075 if (uzbl.state.verbose)
1076 printf ("Searching: %s\n", uzbl.state.searchtx);
1077 webkit_web_view_set_highlight_text_matches (page, TRUE);
1078 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1083 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1085 search_text(page, argv, TRUE);
1089 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1091 search_text(page, argv, FALSE);
1095 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1096 (void) argv; (void) result;
1097 webkit_web_view_set_highlight_text_matches (page, FALSE);
1102 new_window_load_uri (const gchar * uri) {
1103 GString* to_execute = g_string_new ("");
1104 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1106 for (i = 0; entries[i].long_name != NULL; i++) {
1107 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1108 gchar** str = (gchar**)entries[i].arg_data;
1110 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1114 if (uzbl.state.verbose)
1115 printf("\n%s\n", to_execute->str);
1116 g_spawn_command_line_async (to_execute->str, NULL);
1117 g_string_free (to_execute, TRUE);
1121 chain (WebKitWebView *page, GArray *argv, GString *result) {
1122 (void) page; (void) result;
1124 gchar **parts = NULL;
1126 while ((a = argv_idx(argv, i++))) {
1127 parts = g_strsplit (a, " ", 2);
1128 parse_command(parts[0], parts[1], result);
1134 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1138 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1144 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1148 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1154 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1159 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1161 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1166 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1173 /* --Statusbar functions-- */
1175 build_progressbar_ascii(int percent) {
1176 int width=uzbl.gui.sbar.progress_w;
1179 GString *bar = g_string_new("");
1181 l = (double)percent*((double)width/100.);
1182 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1184 for(i=0; i<(int)l; i++)
1185 g_string_append(bar, uzbl.gui.sbar.progress_s);
1188 g_string_append(bar, uzbl.gui.sbar.progress_u);
1190 return g_string_free(bar, FALSE);
1195 const GScannerConfig scan_config = {
1198 ) /* cset_skip_characters */,
1203 ) /* cset_identifier_first */,
1210 ) /* cset_identifier_nth */,
1211 ( "" ) /* cpair_comment_single */,
1213 TRUE /* case_sensitive */,
1215 FALSE /* skip_comment_multi */,
1216 FALSE /* skip_comment_single */,
1217 FALSE /* scan_comment_multi */,
1218 TRUE /* scan_identifier */,
1219 TRUE /* scan_identifier_1char */,
1220 FALSE /* scan_identifier_NULL */,
1221 TRUE /* scan_symbols */,
1222 FALSE /* scan_binary */,
1223 FALSE /* scan_octal */,
1224 FALSE /* scan_float */,
1225 FALSE /* scan_hex */,
1226 FALSE /* scan_hex_dollar */,
1227 FALSE /* scan_string_sq */,
1228 FALSE /* scan_string_dq */,
1229 TRUE /* numbers_2_int */,
1230 FALSE /* int_2_float */,
1231 FALSE /* identifier_2_string */,
1232 FALSE /* char_2_token */,
1233 FALSE /* symbol_2_token */,
1234 TRUE /* scope_0_fallback */,
1239 uzbl.scan = g_scanner_new(&scan_config);
1240 while(symp->symbol_name) {
1241 g_scanner_scope_add_symbol(uzbl.scan, 0,
1243 GINT_TO_POINTER(symp->symbol_token));
1249 expand_template(const char *template, gboolean escape_markup) {
1250 if(!template) return NULL;
1252 GTokenType token = G_TOKEN_NONE;
1253 GString *ret = g_string_new("");
1257 g_scanner_input_text(uzbl.scan, template, strlen(template));
1258 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1259 token = g_scanner_get_next_token(uzbl.scan);
1261 if(token == G_TOKEN_SYMBOL) {
1262 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1266 buf = uzbl.state.keycmd->str?
1267 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1268 g_string_append(ret, buf);
1272 g_string_append(ret, uzbl.state.keycmd->str?
1273 uzbl.state.keycmd->str:g_strdup(""));
1279 else if(token == G_TOKEN_INT) {
1280 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1281 g_string_append(ret, buf);
1284 else if(token == G_TOKEN_IDENTIFIER) {
1285 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1287 else if(token == G_TOKEN_CHAR) {
1288 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1292 return g_string_free(ret, FALSE);
1294 /* --End Statusbar functions-- */
1297 sharg_append(GArray *a, const gchar *str) {
1298 const gchar *s = (str ? str : "");
1299 g_array_append_val(a, s);
1302 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1304 run_command (const gchar *command, const guint npre, const gchar **args,
1305 const gboolean sync, char **output_stdout) {
1306 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1309 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1310 gchar *pid = itos(getpid());
1311 gchar *xwin = itos(uzbl.xwin);
1313 sharg_append(a, command);
1314 for (i = 0; i < npre; i++) /* add n args before the default vars */
1315 sharg_append(a, args[i]);
1316 sharg_append(a, uzbl.state.config_file);
1317 sharg_append(a, pid);
1318 sharg_append(a, xwin);
1319 sharg_append(a, uzbl.comm.fifo_path);
1320 sharg_append(a, uzbl.comm.socket_path);
1321 sharg_append(a, uzbl.state.uri);
1322 sharg_append(a, uzbl.gui.main_title);
1324 for (i = npre; i < g_strv_length((gchar**)args); i++)
1325 sharg_append(a, args[i]);
1329 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1331 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1332 NULL, NULL, output_stdout, NULL, NULL, &err);
1333 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1334 NULL, NULL, NULL, &err);
1336 if (uzbl.state.verbose) {
1337 GString *s = g_string_new("spawned:");
1338 for (i = 0; i < (a->len); i++) {
1339 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1340 g_string_append_printf(s, " %s", qarg);
1343 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1344 printf("%s\n", s->str);
1345 g_string_free(s, TRUE);
1347 printf("Stdout: %s\n", *output_stdout);
1351 g_printerr("error on run_command: %s\n", err->message);
1356 g_array_free (a, TRUE);
1361 split_quoted(const gchar* src, const gboolean unquote) {
1362 /* split on unquoted space, return array of strings;
1363 remove a layer of quotes and backslashes if unquote */
1364 if (!src) return NULL;
1366 gboolean dq = FALSE;
1367 gboolean sq = FALSE;
1368 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1369 GString *s = g_string_new ("");
1373 for (p = src; *p != '\0'; p++) {
1374 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1375 else if (*p == '\\') { g_string_append_c(s, *p++);
1376 g_string_append_c(s, *p); }
1377 else if ((*p == '"') && unquote && !sq) dq = !dq;
1378 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1380 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1381 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1383 else if ((*p == ' ') && !dq && !sq) {
1384 dup = g_strdup(s->str);
1385 g_array_append_val(a, dup);
1386 g_string_truncate(s, 0);
1387 } else g_string_append_c(s, *p);
1389 dup = g_strdup(s->str);
1390 g_array_append_val(a, dup);
1391 ret = (gchar**)a->data;
1392 g_array_free (a, FALSE);
1393 g_string_free (s, TRUE);
1398 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1399 (void)web_view; (void)result;
1400 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1401 if (argv_idx(argv, 0))
1402 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1406 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1407 (void)web_view; (void)result;
1409 if (argv_idx(argv, 0))
1410 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1411 TRUE, &uzbl.comm.sync_stdout);
1415 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1416 (void)web_view; (void)result;
1417 if (!uzbl.behave.shell_cmd) {
1418 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1423 gchar *spacer = g_strdup("");
1424 g_array_insert_val(argv, 1, spacer);
1425 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1427 for (i = 1; i < g_strv_length(cmd); i++)
1428 g_array_prepend_val(argv, cmd[i]);
1430 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1436 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1437 (void)web_view; (void)result;
1438 if (!uzbl.behave.shell_cmd) {
1439 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1444 gchar *spacer = g_strdup("");
1445 g_array_insert_val(argv, 1, spacer);
1446 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1448 for (i = 1; i < g_strv_length(cmd); i++)
1449 g_array_prepend_val(argv, cmd[i]);
1451 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1452 TRUE, &uzbl.comm.sync_stdout);
1458 parse_command(const char *cmd, const char *param, GString *result) {
1461 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1463 gchar **par = split_quoted(param, TRUE);
1464 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1466 if (c->no_split) { /* don't split */
1467 sharg_append(a, param);
1469 for (i = 0; i < g_strv_length(par); i++)
1470 sharg_append(a, par[i]);
1473 if (result == NULL) {
1474 GString *result_print = g_string_new("");
1476 c->function(uzbl.gui.web_view, a, result_print);
1477 if (result_print->len)
1478 printf("%*s\n", result_print->len, result_print->str);
1480 g_string_free(result_print, TRUE);
1482 c->function(uzbl.gui.web_view, a, result);
1485 g_array_free (a, TRUE);
1488 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1495 if(*uzbl.net.proxy_url == ' '
1496 || uzbl.net.proxy_url == NULL) {
1497 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1498 (GType) SOUP_SESSION_PROXY_URI);
1501 suri = soup_uri_new(uzbl.net.proxy_url);
1502 g_object_set(G_OBJECT(uzbl.net.soup_session),
1503 SOUP_SESSION_PROXY_URI,
1505 soup_uri_free(suri);
1512 if(file_exists(uzbl.gui.icon)) {
1513 if (uzbl.gui.main_window)
1514 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1516 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1518 g_free (uzbl.gui.icon);
1523 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1524 g_array_append_val (a, uzbl.state.uri);
1525 load_uri(uzbl.gui.web_view, a, NULL);
1526 g_array_free (a, TRUE);
1530 cmd_always_insert_mode() {
1531 set_insert_mode(uzbl.behave.always_insert_mode);
1537 g_object_set(G_OBJECT(uzbl.net.soup_session),
1538 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1542 cmd_max_conns_host() {
1543 g_object_set(G_OBJECT(uzbl.net.soup_session),
1544 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1549 soup_session_remove_feature
1550 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1551 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1552 /*g_free(uzbl.net.soup_logger);*/
1554 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1555 soup_session_add_feature(uzbl.net.soup_session,
1556 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1559 static WebKitWebSettings*
1561 return webkit_web_view_get_settings(uzbl.gui.web_view);
1566 WebKitWebSettings *ws = view_settings();
1567 if (uzbl.behave.font_size > 0) {
1568 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1571 if (uzbl.behave.monospace_size > 0) {
1572 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1573 uzbl.behave.monospace_size, NULL);
1575 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1576 uzbl.behave.font_size, NULL);
1582 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1586 cmd_disable_plugins() {
1587 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1588 !uzbl.behave.disable_plugins, NULL);
1592 cmd_disable_scripts() {
1593 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1594 !uzbl.behave.disable_scripts, NULL);
1598 cmd_minimum_font_size() {
1599 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1600 uzbl.behave.minimum_font_size, NULL);
1603 cmd_autoload_img() {
1604 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1605 uzbl.behave.autoload_img, NULL);
1610 cmd_autoshrink_img() {
1611 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1612 uzbl.behave.autoshrink_img, NULL);
1617 cmd_enable_spellcheck() {
1618 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1619 uzbl.behave.enable_spellcheck, NULL);
1623 cmd_enable_private() {
1624 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1625 uzbl.behave.enable_private, NULL);
1630 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1631 uzbl.behave.print_bg, NULL);
1636 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1637 uzbl.behave.style_uri, NULL);
1641 cmd_resizable_txt() {
1642 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1643 uzbl.behave.resizable_txt, NULL);
1647 cmd_default_encoding() {
1648 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1649 uzbl.behave.default_encoding, NULL);
1653 cmd_enforce_96dpi() {
1654 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1655 uzbl.behave.enforce_96dpi, NULL);
1659 cmd_caret_browsing() {
1660 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1661 uzbl.behave.caret_browsing, NULL);
1665 cmd_cookie_handler() {
1666 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1667 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1668 if ((g_strcmp0(split[0], "sh") == 0) ||
1669 (g_strcmp0(split[0], "spawn") == 0)) {
1670 g_free (uzbl.behave.cookie_handler);
1671 uzbl.behave.cookie_handler =
1672 g_strdup_printf("sync_%s %s", split[0], split[1]);
1679 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1684 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1689 if(uzbl.behave.inject_html) {
1690 webkit_web_view_load_html_string (uzbl.gui.web_view,
1691 uzbl.behave.inject_html, NULL);
1700 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1701 uzbl.behave.modmask = 0;
1703 if(uzbl.behave.modkey)
1704 g_free(uzbl.behave.modkey);
1705 uzbl.behave.modkey = buf;
1707 for (i = 0; modkeys[i].key != NULL; i++) {
1708 if (g_strrstr(buf, modkeys[i].key))
1709 uzbl.behave.modmask |= modkeys[i].mask;
1715 if (*uzbl.net.useragent == ' ') {
1716 g_free (uzbl.net.useragent);
1717 uzbl.net.useragent = NULL;
1719 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1720 uzbl.net.useragent, NULL);
1726 gtk_widget_ref(uzbl.gui.scrolled_win);
1727 gtk_widget_ref(uzbl.gui.mainbar);
1728 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1729 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1731 if(uzbl.behave.status_top) {
1732 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1733 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1736 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1737 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1739 gtk_widget_unref(uzbl.gui.scrolled_win);
1740 gtk_widget_unref(uzbl.gui.mainbar);
1741 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1746 set_var_value(gchar *name, gchar *val) {
1747 uzbl_cmdprop *c = NULL;
1751 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1752 if(!c->writeable) return TRUE;
1754 /* check for the variable type */
1755 if (c->type == TYPE_STR) {
1756 buf = expand(val, 0, FALSE);
1759 } else if(c->type == TYPE_INT) {
1760 int *ip = (int *)c->ptr;
1761 buf = expand(val, 0, FALSE);
1762 *ip = (int)strtoul(buf, &endp, 10);
1764 } else if (c->type == TYPE_FLOAT) {
1765 float *fp = (float *)c->ptr;
1766 buf = expand(val, 0, FALSE);
1767 *fp = strtod(buf, &endp);
1771 /* invoke a command specific function */
1772 if(c->func) c->func();
1779 Behaviour *b = &uzbl.behave;
1781 if(b->html_buffer->str) {
1782 webkit_web_view_load_html_string (uzbl.gui.web_view,
1783 b->html_buffer->str, b->base_url);
1784 g_string_free(b->html_buffer, TRUE);
1785 b->html_buffer = g_string_new("");
1789 enum {M_CMD, M_HTML};
1791 parse_cmd_line(const char *ctl_line, GString *result) {
1792 Behaviour *b = &uzbl.behave;
1795 if(b->mode == M_HTML) {
1796 len = strlen(b->html_endmarker);
1797 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1798 if(len == strlen(ctl_line)-1 &&
1799 !strncmp(b->html_endmarker, ctl_line, len)) {
1801 set_var_value("mode", "0");
1806 set_timeout(b->html_timeout);
1807 g_string_append(b->html_buffer, ctl_line);
1810 else if((ctl_line[0] == '#') /* Comments */
1811 || (ctl_line[0] == ' ')
1812 || (ctl_line[0] == '\n'))
1813 ; /* ignore these lines */
1814 else { /* parse a command */
1816 gchar **tokens = NULL;
1817 len = strlen(ctl_line);
1819 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1820 ctlstrip = g_strndup(ctl_line, len - 1);
1821 else ctlstrip = g_strdup(ctl_line);
1823 tokens = g_strsplit(ctlstrip, " ", 2);
1824 parse_command(tokens[0], tokens[1], result);
1831 build_stream_name(int type, const gchar* dir) {
1832 State *s = &uzbl.state;
1836 str = g_strdup_printf
1837 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1838 } else if (type == SOCKET) {
1839 str = g_strdup_printf
1840 ("%s/uzbl_socket_%s", dir, s->instance_name);
1846 control_fifo(GIOChannel *gio, GIOCondition condition) {
1847 if (uzbl.state.verbose)
1848 printf("triggered\n");
1853 if (condition & G_IO_HUP)
1854 g_error ("Fifo: Read end of pipe died!\n");
1857 g_error ("Fifo: GIOChannel broke\n");
1859 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1860 if (ret == G_IO_STATUS_ERROR) {
1861 g_error ("Fifo: Error reading: %s\n", err->message);
1865 parse_cmd_line(ctl_line, NULL);
1872 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1873 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1874 if (unlink(uzbl.comm.fifo_path) == -1)
1875 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1876 g_free(uzbl.comm.fifo_path);
1877 uzbl.comm.fifo_path = NULL;
1880 if (*dir == ' ') { /* space unsets the variable */
1885 GIOChannel *chan = NULL;
1886 GError *error = NULL;
1887 gchar *path = build_stream_name(FIFO, dir);
1889 if (!file_exists(path)) {
1890 if (mkfifo (path, 0666) == 0) {
1891 // 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.
1892 chan = g_io_channel_new_file(path, "r+", &error);
1894 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1895 if (uzbl.state.verbose)
1896 printf ("init_fifo: created successfully as %s\n", path);
1897 uzbl.comm.fifo_path = path;
1899 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1900 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1901 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1902 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1904 /* if we got this far, there was an error; cleanup */
1905 if (error) g_error_free (error);
1912 control_stdin(GIOChannel *gio, GIOCondition condition) {
1914 gchar *ctl_line = NULL;
1917 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1918 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1921 parse_cmd_line(ctl_line, NULL);
1929 GIOChannel *chan = NULL;
1930 GError *error = NULL;
1932 chan = g_io_channel_unix_new(fileno(stdin));
1934 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1935 g_error ("Stdin: could not add watch\n");
1937 if (uzbl.state.verbose)
1938 printf ("Stdin: watch added successfully\n");
1941 g_error ("Stdin: Error while opening: %s\n", error->message);
1943 if (error) g_error_free (error);
1947 control_socket(GIOChannel *chan) {
1948 struct sockaddr_un remote;
1949 unsigned int t = sizeof(remote);
1951 GIOChannel *clientchan;
1953 clientsock = accept (g_io_channel_unix_get_fd(chan),
1954 (struct sockaddr *) &remote, &t);
1956 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1957 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1958 (GIOFunc) control_client_socket, clientchan);
1965 control_client_socket(GIOChannel *clientchan) {
1967 GString *result = g_string_new("");
1968 GError *error = NULL;
1972 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1973 if (ret == G_IO_STATUS_ERROR) {
1974 g_warning ("Error reading: %s\n", error->message);
1975 g_io_channel_shutdown(clientchan, TRUE, &error);
1977 } else if (ret == G_IO_STATUS_EOF) {
1978 /* shutdown and remove channel watch from main loop */
1979 g_io_channel_shutdown(clientchan, TRUE, &error);
1984 parse_cmd_line (ctl_line, result);
1985 g_string_append_c(result, '\n');
1986 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1988 if (ret == G_IO_STATUS_ERROR) {
1989 g_warning ("Error writing: %s", error->message);
1991 g_io_channel_flush(clientchan, &error);
1994 if (error) g_error_free (error);
1995 g_string_free(result, TRUE);
2001 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2002 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2003 if (unlink(uzbl.comm.socket_path) == -1)
2004 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2005 g_free(uzbl.comm.socket_path);
2006 uzbl.comm.socket_path = NULL;
2014 GIOChannel *chan = NULL;
2016 struct sockaddr_un local;
2017 gchar *path = build_stream_name(SOCKET, dir);
2019 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2021 local.sun_family = AF_UNIX;
2022 strcpy (local.sun_path, path);
2023 unlink (local.sun_path);
2025 len = strlen (local.sun_path) + sizeof (local.sun_family);
2026 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2027 if (uzbl.state.verbose)
2028 printf ("init_socket: opened in %s\n", path);
2031 if( (chan = g_io_channel_unix_new(sock)) ) {
2032 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2033 uzbl.comm.socket_path = path;
2036 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2038 /* if we got this far, there was an error; cleanup */
2045 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2046 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2048 // this function may be called very early when the templates are not set (yet), hence the checks
2050 update_title (void) {
2051 Behaviour *b = &uzbl.behave;
2054 if (b->show_status) {
2055 if (b->title_format_short) {
2056 parsed = expand_template(b->title_format_short, FALSE);
2057 parsed = expand(parsed, 0, FALSE);
2058 if (uzbl.gui.main_window)
2059 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2062 if (b->status_format) {
2063 parsed = expand_template(b->status_format, TRUE);
2064 parsed = expand(parsed, 0, TRUE);
2065 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2068 if (b->status_background) {
2070 gdk_color_parse (b->status_background, &color);
2071 //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)
2072 if (uzbl.gui.main_window)
2073 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2074 else if (uzbl.gui.plug)
2075 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2078 if (b->title_format_long) {
2079 parsed = expand_template(b->title_format_long, FALSE);
2080 parsed = expand(parsed, 0, FALSE);
2081 if (uzbl.gui.main_window)
2082 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2089 key_press_cb (GtkWidget* window, GdkEventKey* event)
2091 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2095 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2096 || 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)
2099 /* turn off insert mode (if always_insert_mode is not used) */
2100 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2101 set_insert_mode(uzbl.behave.always_insert_mode);
2106 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2109 if (event->keyval == GDK_Escape) {
2110 g_string_truncate(uzbl.state.keycmd, 0);
2112 dehilight(uzbl.gui.web_view, NULL, NULL);
2116 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2117 if (event->keyval == GDK_Insert) {
2119 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2120 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2122 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2125 g_string_append (uzbl.state.keycmd, str);
2132 if (event->keyval == GDK_BackSpace)
2133 keycmd_bs(NULL, NULL, NULL);
2135 gboolean key_ret = FALSE;
2136 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2138 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2140 run_keycmd(key_ret);
2142 if (key_ret) return (!uzbl.behave.insert_mode);
2147 run_keycmd(const gboolean key_ret) {
2148 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2150 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2151 g_string_truncate(uzbl.state.keycmd, 0);
2152 parse_command(act->name, act->param, NULL);
2156 /* try if it's an incremental keycmd or one that takes args, and run it */
2157 GString* short_keys = g_string_new ("");
2158 GString* short_keys_inc = g_string_new ("");
2160 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2161 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2162 g_string_assign(short_keys_inc, short_keys->str);
2163 g_string_append_c(short_keys, '_');
2164 g_string_append_c(short_keys_inc, '*');
2166 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2167 /* run normal cmds only if return was pressed */
2168 exec_paramcmd(act, i);
2169 g_string_truncate(uzbl.state.keycmd, 0);
2171 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2172 if (key_ret) /* just quit the incremental command on return */
2173 g_string_truncate(uzbl.state.keycmd, 0);
2174 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2178 g_string_truncate(short_keys, short_keys->len - 1);
2180 g_string_free (short_keys, TRUE);
2181 g_string_free (short_keys_inc, TRUE);
2185 exec_paramcmd(const Action *act, const guint i) {
2186 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2187 GString *actionname = g_string_new ("");
2188 GString *actionparam = g_string_new ("");
2189 g_string_erase (parampart, 0, i+1);
2191 g_string_printf (actionname, act->name, parampart->str);
2193 g_string_printf (actionparam, act->param, parampart->str);
2194 parse_command(actionname->str, actionparam->str, NULL);
2195 g_string_free(actionname, TRUE);
2196 g_string_free(actionparam, TRUE);
2197 g_string_free(parampart, TRUE);
2205 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2206 //main_window_ref = g_object_ref(scrolled_window);
2207 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
2209 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2210 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2212 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2213 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2214 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2215 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2216 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2217 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2218 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2219 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2220 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2221 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2222 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2224 return scrolled_window;
2231 g->mainbar = gtk_hbox_new (FALSE, 0);
2233 /* keep a reference to the bar so we can re-pack it at runtime*/
2234 //sbar_ref = g_object_ref(g->mainbar);
2236 g->mainbar_label = gtk_label_new ("");
2237 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2238 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2239 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2240 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2241 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2242 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2247 GtkWidget* create_window () {
2248 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2249 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2250 gtk_widget_set_name (window, "Uzbl browser");
2251 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2252 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2258 GtkPlug* create_plug () {
2259 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2260 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2261 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2268 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2270 If actname is one that calls an external command, this function will inject
2271 newargs in front of the user-provided args in that command line. They will
2272 come become after the body of the script (in sh) or after the name of
2273 the command to execute (in spawn).
2274 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2275 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2277 The return value consist of two strings: the action (sh, ...) and its args.
2279 If act is not one that calls an external command, then the given action merely
2282 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2283 gchar *actdup = g_strdup(actname);
2284 g_array_append_val(rets, actdup);
2286 if ((g_strcmp0(actname, "spawn") == 0) ||
2287 (g_strcmp0(actname, "sh") == 0) ||
2288 (g_strcmp0(actname, "sync_spawn") == 0) ||
2289 (g_strcmp0(actname, "sync_sh") == 0)) {
2291 GString *a = g_string_new("");
2292 gchar **spawnparts = split_quoted(origargs, FALSE);
2293 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2294 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2296 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2297 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2299 g_array_append_val(rets, a->str);
2300 g_string_free(a, FALSE);
2301 g_strfreev(spawnparts);
2303 gchar *origdup = g_strdup(origargs);
2304 g_array_append_val(rets, origdup);
2306 return (gchar**)g_array_free(rets, FALSE);
2310 run_handler (const gchar *act, const gchar *args) {
2311 /* Consider this code a temporary hack to make the handlers usable.
2312 In practice, all this splicing, injection, and reconstruction is
2313 inefficient, annoying and hard to manage. Potential pitfalls arise
2314 when the handler specific args 1) are not quoted (the handler
2315 callbacks should take care of this) 2) are quoted but interfere
2316 with the users' own quotation. A more ideal solution is
2317 to refactor parse_command so that it doesn't just take a string
2318 and execute it; rather than that, we should have a function which
2319 returns the argument vector parsed from the string. This vector
2320 could be modified (e.g. insert additional args into it) before
2321 passing it to the next function that actually executes it. Though
2322 it still isn't perfect for chain actions.. will reconsider & re-
2323 factor when I have the time. -duc */
2325 char **parts = g_strsplit(act, " ", 2);
2327 if (g_strcmp0(parts[0], "chain") == 0) {
2328 GString *newargs = g_string_new("");
2329 gchar **chainparts = split_quoted(parts[1], FALSE);
2331 /* for every argument in the chain, inject the handler args
2332 and make sure the new parts are wrapped in quotes */
2333 gchar **cp = chainparts;
2335 gchar *quotless = NULL;
2336 gchar **spliced_quotless = NULL; // sigh -_-;
2337 gchar **inpart = NULL;
2340 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2342 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2343 } else quotless = g_strdup(*cp);
2345 spliced_quotless = g_strsplit(quotless, " ", 2);
2346 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2347 g_strfreev(spliced_quotless);
2349 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2355 parse_command(parts[0], &(newargs->str[1]), NULL);
2356 g_string_free(newargs, TRUE);
2357 g_strfreev(chainparts);
2360 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2361 parse_command(inparts[0], inparts[1], NULL);
2369 add_binding (const gchar *key, const gchar *act) {
2370 char **parts = g_strsplit(act, " ", 2);
2377 if (uzbl.state.verbose)
2378 printf ("Binding %-10s : %s\n", key, act);
2379 action = new_action(parts[0], parts[1]);
2381 if (g_hash_table_remove (uzbl.bindings, key))
2382 g_warning ("Overwriting existing binding for \"%s\"", key);
2383 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2388 get_xdg_var (XDG_Var xdg) {
2389 const gchar* actual_value = getenv (xdg.environmental);
2390 const gchar* home = getenv ("HOME");
2391 gchar* return_value;
2393 if (! actual_value || strcmp (actual_value, "") == 0) {
2394 if (xdg.default_value) {
2395 return_value = str_replace ("~", home, xdg.default_value);
2397 return_value = NULL;
2400 return_value = str_replace("~", home, actual_value);
2403 return return_value;
2407 find_xdg_file (int xdg_type, char* filename) {
2408 /* xdg_type = 0 => config
2409 xdg_type = 1 => data
2410 xdg_type = 2 => cache*/
2412 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2413 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2416 gchar* temporary_string;
2420 if (! file_exists (temporary_file) && xdg_type != 2) {
2421 buf = get_xdg_var (XDG[3 + xdg_type]);
2422 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2425 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2426 g_free (temporary_file);
2427 temporary_file = g_strconcat (temporary_string, filename, NULL);
2431 //g_free (temporary_string); - segfaults.
2433 if (file_exists (temporary_file)) {
2434 return temporary_file;
2441 State *s = &uzbl.state;
2442 Network *n = &uzbl.net;
2444 for (i = 0; default_config[i].command != NULL; i++) {
2445 parse_cmd_line(default_config[i].command, NULL);
2448 if (g_strcmp0(s->config_file, "-") == 0) {
2449 s->config_file = NULL;
2453 else if (!s->config_file) {
2454 s->config_file = find_xdg_file (0, "/uzbl/config");
2457 if (s->config_file) {
2458 GArray* lines = read_file_by_line (s->config_file);
2462 while ((line = g_array_index(lines, gchar*, i))) {
2463 parse_cmd_line (line, NULL);
2467 g_array_free (lines, TRUE);
2469 if (uzbl.state.verbose)
2470 printf ("No configuration file loaded.\n");
2473 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2476 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2479 if (!uzbl.behave.cookie_handler)
2482 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2483 GString *s = g_string_new ("");
2484 SoupURI * soup_uri = soup_message_get_uri(msg);
2485 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2486 run_handler(uzbl.behave.cookie_handler, s->str);
2488 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2489 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2490 if ( p != NULL ) *p = '\0';
2491 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2493 if (uzbl.comm.sync_stdout)
2494 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2496 g_string_free(s, TRUE);
2500 save_cookies (SoupMessage *msg, gpointer user_data){
2504 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2505 cookie = soup_cookie_to_set_cookie_header(ck->data);
2506 SoupURI * soup_uri = soup_message_get_uri(msg);
2507 GString *s = g_string_new ("");
2508 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2509 run_handler(uzbl.behave.cookie_handler, s->str);
2511 g_string_free(s, TRUE);
2516 /* --- WEBINSPECTOR --- */
2518 hide_window_cb(GtkWidget *widget, gpointer data) {
2521 gtk_widget_hide(widget);
2524 static WebKitWebView*
2525 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2528 (void) web_inspector;
2529 GtkWidget* scrolled_window;
2530 GtkWidget* new_web_view;
2533 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2534 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2535 G_CALLBACK(hide_window_cb), NULL);
2537 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2538 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2539 gtk_widget_show(g->inspector_window);
2541 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2542 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2543 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2544 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2545 gtk_widget_show(scrolled_window);
2547 new_web_view = webkit_web_view_new();
2548 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2550 return WEBKIT_WEB_VIEW(new_web_view);
2554 inspector_show_window_cb (WebKitWebInspector* inspector){
2556 gtk_widget_show(uzbl.gui.inspector_window);
2560 /* TODO: Add variables and code to make use of these functions */
2562 inspector_close_window_cb (WebKitWebInspector* inspector){
2568 inspector_attach_window_cb (WebKitWebInspector* inspector){
2574 inspector_detach_window_cb (WebKitWebInspector* inspector){
2580 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2586 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2592 set_up_inspector() {
2594 WebKitWebSettings *settings = view_settings();
2595 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2597 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2598 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2599 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2600 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2601 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2602 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2603 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2605 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2609 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2611 uzbl_cmdprop *c = v;
2616 if(c->type == TYPE_STR)
2617 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2618 else if(c->type == TYPE_INT)
2619 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2623 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2627 printf("bind %s = %s %s\n", (char *)k ,
2628 (char *)a->name, a->param?(char *)a->param:"");
2633 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2634 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2637 /* set up gtk, gobject, variable defaults and other things that tests and other
2638 * external applications need to do anyhow */
2640 initialize(int argc, char *argv[]) {
2641 gtk_init (&argc, &argv);
2642 if (!g_thread_supported ())
2643 g_thread_init (NULL);
2644 uzbl.state.executable_path = g_strdup(argv[0]);
2645 uzbl.state.selected_url = NULL;
2646 uzbl.state.searchtx = NULL;
2648 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2649 g_option_context_add_main_entries (context, entries, NULL);
2650 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2651 g_option_context_parse (context, &argc, &argv, NULL);
2652 g_option_context_free(context);
2654 /* initialize hash table */
2655 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2657 uzbl.net.soup_session = webkit_get_default_session();
2658 uzbl.state.keycmd = g_string_new("");
2660 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2661 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2662 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2663 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2664 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2665 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2667 uzbl.gui.sbar.progress_s = g_strdup("=");
2668 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2669 uzbl.gui.sbar.progress_w = 10;
2671 /* HTML mode defaults*/
2672 uzbl.behave.html_buffer = g_string_new("");
2673 uzbl.behave.html_endmarker = g_strdup(".");
2674 uzbl.behave.html_timeout = 60;
2675 uzbl.behave.base_url = g_strdup("http://invalid");
2677 /* default mode indicators */
2678 uzbl.behave.insert_indicator = g_strdup("I");
2679 uzbl.behave.cmd_indicator = g_strdup("C");
2681 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2682 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2683 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2684 uzbl.info.arch = ARCH;
2685 uzbl.info.commit = COMMIT;
2689 make_var_to_name_hash();
2691 uzbl.gui.scrolled_win = create_browser();
2694 #ifndef UZBL_LIBRARY
2697 main (int argc, char* argv[]) {
2698 initialize(argc, argv);
2700 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2704 /* initial packing */
2705 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2706 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2708 if (uzbl.state.socket_id) {
2709 uzbl.gui.plug = create_plug ();
2710 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2711 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2713 uzbl.gui.main_window = create_window ();
2714 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2715 gtk_widget_show_all (uzbl.gui.main_window);
2716 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2719 if(!uzbl.state.instance_name)
2720 uzbl.state.instance_name = itos((int)uzbl.xwin);
2722 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2724 if (uzbl.state.verbose) {
2725 printf("Uzbl start location: %s\n", argv[0]);
2726 if (uzbl.state.socket_id)
2727 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2729 printf("window_id %i\n",(int) uzbl.xwin);
2730 printf("pid %i\n", getpid ());
2731 printf("name: %s\n", uzbl.state.instance_name);
2734 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2735 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2736 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2737 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2738 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2742 if (!uzbl.behave.show_status)
2743 gtk_widget_hide(uzbl.gui.mainbar);
2750 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2751 gboolean verbose_override = uzbl.state.verbose;
2753 if (verbose_override > uzbl.state.verbose)
2754 uzbl.state.verbose = verbose_override;
2757 set_var_value("uri", uri_override);
2758 g_free(uri_override);
2759 } else if (uzbl.state.uri)
2760 cmd_load_uri(uzbl.gui.web_view, NULL);
2765 return EXIT_SUCCESS;
2769 /* vi: set et ts=4: */