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 { "MSG", PTR_C(uzbl.gui.sbar.msg, 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 uzbl.behave.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);
888 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
889 (void) page; (void) result;
891 if (argv_idx(argv, 0)) {
892 if (strcmp (argv_idx(argv, 0), "0") == 0) {
893 uzbl.behave.insert_mode = FALSE;
895 uzbl.behave.insert_mode = TRUE;
898 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
905 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
908 if (argv_idx(argv, 0)) {
909 GString* newuri = g_string_new (argv_idx(argv, 0));
910 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
911 run_js(web_view, argv, NULL);
914 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
915 g_string_prepend (newuri, "http://");
916 /* if we do handle cookies, ask our handler for them */
917 webkit_web_view_load_uri (web_view, newuri->str);
918 g_string_free (newuri, TRUE);
926 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
927 size_t argumentCount, const JSValueRef arguments[],
928 JSValueRef* exception) {
933 JSStringRef js_result_string;
934 GString *result = g_string_new("");
936 if (argumentCount >= 1) {
937 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
938 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
939 char ctl_line[arg_size];
940 JSStringGetUTF8CString(arg, ctl_line, arg_size);
942 parse_cmd_line(ctl_line, result);
944 JSStringRelease(arg);
946 js_result_string = JSStringCreateWithUTF8CString(result->str);
948 g_string_free(result, TRUE);
950 return JSValueMakeString(ctx, js_result_string);
953 static JSStaticFunction js_static_functions[] = {
954 {"run", js_run_command, kJSPropertyAttributeNone},
959 /* This function creates the class and its definition, only once */
960 if (!uzbl.js.initialized) {
961 /* it would be pretty cool to make this dynamic */
962 uzbl.js.classdef = kJSClassDefinitionEmpty;
963 uzbl.js.classdef.staticFunctions = js_static_functions;
965 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
971 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
972 WebKitWebFrame *frame;
973 JSGlobalContextRef context;
974 JSObjectRef globalobject;
975 JSStringRef var_name;
977 JSStringRef js_script;
978 JSValueRef js_result;
979 JSStringRef js_result_string;
980 size_t js_result_size;
984 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
985 context = webkit_web_frame_get_global_context(frame);
986 globalobject = JSContextGetGlobalObject(context);
988 /* uzbl javascript namespace */
989 var_name = JSStringCreateWithUTF8CString("Uzbl");
990 JSObjectSetProperty(context, globalobject, var_name,
991 JSObjectMake(context, uzbl.js.classref, NULL),
992 kJSClassAttributeNone, NULL);
994 /* evaluate the script and get return value*/
995 js_script = JSStringCreateWithUTF8CString(script);
996 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
997 if (js_result && !JSValueIsUndefined(context, js_result)) {
998 js_result_string = JSValueToStringCopy(context, js_result, NULL);
999 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1001 if (js_result_size) {
1002 char js_result_utf8[js_result_size];
1003 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1004 g_string_assign(result, js_result_utf8);
1007 JSStringRelease(js_result_string);
1011 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1013 JSStringRelease(var_name);
1014 JSStringRelease(js_script);
1018 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1020 if (argv_idx(argv, 0))
1021 eval_js(web_view, argv_idx(argv, 0), result);
1025 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1027 if (argv_idx(argv, 0)) {
1028 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1033 while ((line = g_array_index(lines, gchar*, i))) {
1035 js = g_strdup (line);
1037 gchar* newjs = g_strconcat (js, line, NULL);
1044 if (uzbl.state.verbose)
1045 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1047 if (argv_idx (argv, 1)) {
1048 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1052 eval_js (web_view, js, result);
1054 g_array_free (lines, TRUE);
1059 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1060 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1061 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1062 webkit_web_view_unmark_text_matches (page);
1063 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1064 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1068 if (uzbl.state.searchtx) {
1069 if (uzbl.state.verbose)
1070 printf ("Searching: %s\n", uzbl.state.searchtx);
1071 webkit_web_view_set_highlight_text_matches (page, TRUE);
1072 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1077 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1079 search_text(page, argv, TRUE);
1083 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1085 search_text(page, argv, FALSE);
1089 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1090 (void) argv; (void) result;
1091 webkit_web_view_set_highlight_text_matches (page, FALSE);
1096 new_window_load_uri (const gchar * uri) {
1097 GString* to_execute = g_string_new ("");
1098 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1100 for (i = 0; entries[i].long_name != NULL; i++) {
1101 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1102 gchar** str = (gchar**)entries[i].arg_data;
1104 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1108 if (uzbl.state.verbose)
1109 printf("\n%s\n", to_execute->str);
1110 g_spawn_command_line_async (to_execute->str, NULL);
1111 g_string_free (to_execute, TRUE);
1115 chain (WebKitWebView *page, GArray *argv, GString *result) {
1116 (void) page; (void) result;
1118 gchar **parts = NULL;
1120 while ((a = argv_idx(argv, i++))) {
1121 parts = g_strsplit (a, " ", 2);
1122 parse_command(parts[0], parts[1], result);
1128 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1132 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1138 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1142 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1148 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1153 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1155 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1160 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1167 /* --Statusbar functions-- */
1169 build_progressbar_ascii(int percent) {
1170 int width=uzbl.gui.sbar.progress_w;
1173 GString *bar = g_string_new("");
1175 l = (double)percent*((double)width/100.);
1176 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1178 for(i=0; i<(int)l; i++)
1179 g_string_append(bar, uzbl.gui.sbar.progress_s);
1182 g_string_append(bar, uzbl.gui.sbar.progress_u);
1184 return g_string_free(bar, FALSE);
1189 const GScannerConfig scan_config = {
1192 ) /* cset_skip_characters */,
1197 ) /* cset_identifier_first */,
1204 ) /* cset_identifier_nth */,
1205 ( "" ) /* cpair_comment_single */,
1207 TRUE /* case_sensitive */,
1209 FALSE /* skip_comment_multi */,
1210 FALSE /* skip_comment_single */,
1211 FALSE /* scan_comment_multi */,
1212 TRUE /* scan_identifier */,
1213 TRUE /* scan_identifier_1char */,
1214 FALSE /* scan_identifier_NULL */,
1215 TRUE /* scan_symbols */,
1216 FALSE /* scan_binary */,
1217 FALSE /* scan_octal */,
1218 FALSE /* scan_float */,
1219 FALSE /* scan_hex */,
1220 FALSE /* scan_hex_dollar */,
1221 FALSE /* scan_string_sq */,
1222 FALSE /* scan_string_dq */,
1223 TRUE /* numbers_2_int */,
1224 FALSE /* int_2_float */,
1225 FALSE /* identifier_2_string */,
1226 FALSE /* char_2_token */,
1227 FALSE /* symbol_2_token */,
1228 TRUE /* scope_0_fallback */,
1233 uzbl.scan = g_scanner_new(&scan_config);
1234 while(symp->symbol_name) {
1235 g_scanner_scope_add_symbol(uzbl.scan, 0,
1237 GINT_TO_POINTER(symp->symbol_token));
1243 expand_template(const char *template, gboolean escape_markup) {
1244 if(!template) return NULL;
1246 GTokenType token = G_TOKEN_NONE;
1247 GString *ret = g_string_new("");
1251 g_scanner_input_text(uzbl.scan, template, strlen(template));
1252 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1253 token = g_scanner_get_next_token(uzbl.scan);
1255 if(token == G_TOKEN_SYMBOL) {
1256 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1260 buf = uzbl.state.keycmd->str?
1261 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1262 g_string_append(ret, buf);
1266 g_string_append(ret, uzbl.state.keycmd->str?
1267 uzbl.state.keycmd->str:g_strdup(""));
1270 g_string_append(ret,
1271 uzbl.behave.insert_mode?
1272 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1278 else if(token == G_TOKEN_INT) {
1279 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1280 g_string_append(ret, buf);
1283 else if(token == G_TOKEN_IDENTIFIER) {
1284 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1286 else if(token == G_TOKEN_CHAR) {
1287 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1291 return g_string_free(ret, FALSE);
1293 /* --End Statusbar functions-- */
1296 sharg_append(GArray *a, const gchar *str) {
1297 const gchar *s = (str ? str : "");
1298 g_array_append_val(a, s);
1301 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1303 run_command (const gchar *command, const guint npre, const gchar **args,
1304 const gboolean sync, char **output_stdout) {
1305 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1308 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1309 gchar *pid = itos(getpid());
1310 gchar *xwin = itos(uzbl.xwin);
1312 sharg_append(a, command);
1313 for (i = 0; i < npre; i++) /* add n args before the default vars */
1314 sharg_append(a, args[i]);
1315 sharg_append(a, uzbl.state.config_file);
1316 sharg_append(a, pid);
1317 sharg_append(a, xwin);
1318 sharg_append(a, uzbl.comm.fifo_path);
1319 sharg_append(a, uzbl.comm.socket_path);
1320 sharg_append(a, uzbl.state.uri);
1321 sharg_append(a, uzbl.gui.main_title);
1323 for (i = npre; i < g_strv_length((gchar**)args); i++)
1324 sharg_append(a, args[i]);
1328 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1330 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1331 NULL, NULL, output_stdout, NULL, NULL, &err);
1332 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1333 NULL, NULL, NULL, &err);
1335 if (uzbl.state.verbose) {
1336 GString *s = g_string_new("spawned:");
1337 for (i = 0; i < (a->len); i++) {
1338 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1339 g_string_append_printf(s, " %s", qarg);
1342 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1343 printf("%s\n", s->str);
1344 g_string_free(s, TRUE);
1346 printf("Stdout: %s\n", *output_stdout);
1350 g_printerr("error on run_command: %s\n", err->message);
1355 g_array_free (a, TRUE);
1360 split_quoted(const gchar* src, const gboolean unquote) {
1361 /* split on unquoted space, return array of strings;
1362 remove a layer of quotes and backslashes if unquote */
1363 if (!src) return NULL;
1365 gboolean dq = FALSE;
1366 gboolean sq = FALSE;
1367 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1368 GString *s = g_string_new ("");
1372 for (p = src; *p != '\0'; p++) {
1373 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1374 else if (*p == '\\') { g_string_append_c(s, *p++);
1375 g_string_append_c(s, *p); }
1376 else if ((*p == '"') && unquote && !sq) dq = !dq;
1377 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1379 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1380 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1382 else if ((*p == ' ') && !dq && !sq) {
1383 dup = g_strdup(s->str);
1384 g_array_append_val(a, dup);
1385 g_string_truncate(s, 0);
1386 } else g_string_append_c(s, *p);
1388 dup = g_strdup(s->str);
1389 g_array_append_val(a, dup);
1390 ret = (gchar**)a->data;
1391 g_array_free (a, FALSE);
1392 g_string_free (s, TRUE);
1397 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1398 (void)web_view; (void)result;
1399 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1400 if (argv_idx(argv, 0))
1401 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1405 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1406 (void)web_view; (void)result;
1408 if (argv_idx(argv, 0))
1409 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1410 TRUE, &uzbl.comm.sync_stdout);
1414 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1415 (void)web_view; (void)result;
1416 if (!uzbl.behave.shell_cmd) {
1417 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1422 gchar *spacer = g_strdup("");
1423 g_array_insert_val(argv, 1, spacer);
1424 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1426 for (i = 1; i < g_strv_length(cmd); i++)
1427 g_array_prepend_val(argv, cmd[i]);
1429 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1435 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1436 (void)web_view; (void)result;
1437 if (!uzbl.behave.shell_cmd) {
1438 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1443 gchar *spacer = g_strdup("");
1444 g_array_insert_val(argv, 1, spacer);
1445 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1447 for (i = 1; i < g_strv_length(cmd); i++)
1448 g_array_prepend_val(argv, cmd[i]);
1450 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1451 TRUE, &uzbl.comm.sync_stdout);
1457 parse_command(const char *cmd, const char *param, GString *result) {
1460 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1462 gchar **par = split_quoted(param, TRUE);
1463 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1465 if (c->no_split) { /* don't split */
1466 sharg_append(a, param);
1468 for (i = 0; i < g_strv_length(par); i++)
1469 sharg_append(a, par[i]);
1472 if (result == NULL) {
1473 GString *result_print = g_string_new("");
1475 c->function(uzbl.gui.web_view, a, result_print);
1476 if (result_print->len)
1477 printf("%*s\n", result_print->len, result_print->str);
1479 g_string_free(result_print, TRUE);
1481 c->function(uzbl.gui.web_view, a, result);
1484 g_array_free (a, TRUE);
1487 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1494 if(*uzbl.net.proxy_url == ' '
1495 || uzbl.net.proxy_url == NULL) {
1496 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1497 (GType) SOUP_SESSION_PROXY_URI);
1500 suri = soup_uri_new(uzbl.net.proxy_url);
1501 g_object_set(G_OBJECT(uzbl.net.soup_session),
1502 SOUP_SESSION_PROXY_URI,
1504 soup_uri_free(suri);
1511 if(file_exists(uzbl.gui.icon)) {
1512 if (uzbl.gui.main_window)
1513 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1515 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1517 g_free (uzbl.gui.icon);
1522 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1523 g_array_append_val (a, uzbl.state.uri);
1524 load_uri(uzbl.gui.web_view, a, NULL);
1525 g_array_free (a, TRUE);
1529 cmd_always_insert_mode() {
1530 uzbl.behave.insert_mode =
1531 uzbl.behave.always_insert_mode ? TRUE : FALSE;
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 uzbl.behave.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: */