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 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
79 "Print the version and exit", NULL },
80 { NULL, 0, 0, 0, NULL, NULL, NULL }
83 /* associate command names to their properties */
84 typedef const struct {
92 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
94 /* abbreviations to help keep the table's width humane */
95 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
96 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
101 } var_name_to_ptr[] = {
102 /* variable name pointer to variable in code type dump callback function */
103 /* ---------------------------------------------------------------------------------------------- */
104 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
105 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
106 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
107 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
108 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
109 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
110 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
111 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, NULL)}, /* XXX */
112 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
113 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
114 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
115 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
116 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
117 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
118 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
119 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
120 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_title)},
121 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
122 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
123 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
124 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
125 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
126 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
127 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
128 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
129 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
130 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
131 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
132 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
133 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
134 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
135 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
136 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
137 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
138 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
139 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
140 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
141 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
142 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
143 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
144 /* exported WebKitWebSettings properties */
145 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
146 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
147 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
148 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
149 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
150 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
151 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
152 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
153 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
154 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
155 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
156 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
157 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
158 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
159 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
160 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
162 /* constants (not dumpable or writeable) */
163 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
164 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
165 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
166 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
167 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
168 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
169 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
170 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
171 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
172 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
173 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
175 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
176 }, *n2v_p = var_name_to_ptr;
183 { "SHIFT", GDK_SHIFT_MASK }, // shift
184 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
185 { "CONTROL", GDK_CONTROL_MASK }, // control
186 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
187 { "MOD2", GDK_MOD2_MASK }, // 5th mod
188 { "MOD3", GDK_MOD3_MASK }, // 6th mod
189 { "MOD4", GDK_MOD4_MASK }, // 7th mod
190 { "MOD5", GDK_MOD5_MASK }, // 8th mod
191 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
192 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
193 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
194 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
195 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
196 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
197 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
198 { "META", GDK_META_MASK }, // meta (since 2.10)
203 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
204 * for quick access */
206 make_var_to_name_hash() {
207 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
209 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
214 /* --- UTILITY FUNCTIONS --- */
216 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
218 get_exp_type(gchar *s) {
222 else if(*(s+1) == '{')
223 return EXP_BRACED_VAR;
224 else if(*(s+1) == '<')
226 else if(*(s+1) == '[')
229 return EXP_SIMPLE_VAR;
235 * recurse == 1: don't expand '@(command)@'
236 * recurse == 2: don't expand '@<java script>@'
239 expand(char *s, guint recurse) {
243 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
248 gchar *cmd_stdout = NULL;
250 GString *buf = g_string_new("");
251 GString *js_ret = g_string_new("");
256 g_string_append_c(buf, *++s);
261 etype = get_exp_type(s);
266 vend = strpbrk(s, end_simple_var);
267 if(!vend) vend = strchr(s, '\0');
271 vend = strchr(s, upto);
272 if(!vend) vend = strchr(s, '\0');
276 strcpy(str_end, ")@");
278 vend = strstr(s, str_end);
279 if(!vend) vend = strchr(s, '\0');
283 strcpy(str_end, ">@");
285 vend = strstr(s, str_end);
286 if(!vend) vend = strchr(s, '\0');
290 strcpy(str_end, "]@");
292 vend = strstr(s, str_end);
293 if(!vend) vend = strchr(s, '\0');
298 strncpy(ret, s, vend-s);
302 if(etype == EXP_SIMPLE_VAR ||
303 etype == EXP_BRACED_VAR) {
304 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
305 if(c->type == TYPE_STR && *c->ptr != NULL) {
306 g_string_append(buf, (gchar *)*c->ptr);
307 } else if(c->type == TYPE_INT) {
308 char *b = itos((uintptr_t)*c->ptr);
309 g_string_append(buf, b);
314 if(etype == EXP_SIMPLE_VAR)
319 else if(recurse != 1 &&
321 mycmd = expand(ret, 1);
322 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
326 g_printerr("error on running command: %s\n", err->message);
329 else if (*cmd_stdout) {
330 int len = strlen(cmd_stdout);
332 if(cmd_stdout[len-1] == '\n')
333 cmd_stdout[--len] = 0; /* strip trailing newline */
335 g_string_append(buf, cmd_stdout);
340 else if(recurse != 2 &&
342 mycmd = expand(ret, 2);
343 eval_js(uzbl.gui.web_view, mycmd, js_ret);
347 g_string_append(buf, js_ret->str);
348 g_string_free(js_ret, TRUE);
349 js_ret = g_string_new("");
353 else if(etype == EXP_ESCAPE) {
354 mycmd = expand(ret, 0);
355 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
357 g_string_append(buf, escaped);
366 g_string_append_c(buf, *s);
371 g_string_free(js_ret, TRUE);
372 return g_string_free(buf, FALSE);
379 snprintf(tmp, sizeof(tmp), "%i", val);
380 return g_strdup(tmp);
384 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
387 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
390 str_replace (const char* search, const char* replace, const char* string) {
394 buf = g_strsplit (string, search, -1);
395 ret = g_strjoinv (replace, buf);
396 g_strfreev(buf); // somebody said this segfaults
402 read_file_by_line (gchar *path) {
403 GIOChannel *chan = NULL;
404 gchar *readbuf = NULL;
406 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
409 chan = g_io_channel_new_file(path, "r", NULL);
412 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
413 const gchar* val = g_strdup (readbuf);
414 g_array_append_val (lines, val);
419 g_io_channel_unref (chan);
421 fprintf(stderr, "File '%s' not be read.\n", path);
428 gchar* parseenv (char* string) {
429 extern char** environ;
430 gchar* tmpstr = NULL;
434 while (environ[i] != NULL) {
435 gchar** env = g_strsplit (environ[i], "=", 2);
436 gchar* envname = g_strconcat ("$", env[0], NULL);
438 if (g_strrstr (string, envname) != NULL) {
439 tmpstr = g_strdup(string);
441 string = str_replace(envname, env[1], tmpstr);
446 g_strfreev (env); // somebody said this breaks uzbl
454 setup_signal(int signr, sigfunc *shandler) {
455 struct sigaction nh, oh;
457 nh.sa_handler = shandler;
458 sigemptyset(&nh.sa_mask);
461 if(sigaction(signr, &nh, &oh) < 0)
469 if (uzbl.behave.fifo_dir)
470 unlink (uzbl.comm.fifo_path);
471 if (uzbl.behave.socket_dir)
472 unlink (uzbl.comm.socket_path);
474 g_free(uzbl.state.executable_path);
475 g_free(uzbl.state.keycmd);
476 g_hash_table_destroy(uzbl.bindings);
477 g_hash_table_destroy(uzbl.behave.commands);
480 /* used for html_mode_timeout
481 * be sure to extend this function to use
482 * more timers if needed in other places
485 set_timeout(int seconds) {
487 memset(&t, 0, sizeof t);
489 t.it_value.tv_sec = seconds;
490 t.it_value.tv_usec = 0;
491 setitimer(ITIMER_REAL, &t, NULL);
494 /* --- SIGNAL HANDLER --- */
497 catch_sigterm(int s) {
503 catch_sigint(int s) {
513 set_var_value("mode", "0");
518 /* --- CALLBACKS --- */
521 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
524 (void) navigation_action;
525 (void) policy_decision;
527 const gchar* uri = webkit_network_request_get_uri (request);
528 if (uzbl.state.verbose)
529 printf("New window requested -> %s \n", uri);
530 webkit_web_policy_decision_use(policy_decision);
535 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
540 /* If we can display it, let's display it... */
541 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
542 webkit_web_policy_decision_use (policy_decision);
546 /* ...everything we can't displayed is downloaded */
547 webkit_web_policy_decision_download (policy_decision);
552 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
556 if (uzbl.state.selected_url != NULL) {
557 if (uzbl.state.verbose)
558 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
559 new_window_load_uri(uzbl.state.selected_url);
561 if (uzbl.state.verbose)
562 printf("New web view -> %s\n","Nothing to open, exiting");
568 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
571 if (uzbl.behave.download_handler) {
572 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
573 if (uzbl.state.verbose)
574 printf("Download -> %s\n",uri);
575 /* if urls not escaped, we may have to escape and quote uri before this call */
576 run_handler(uzbl.behave.download_handler, uri);
581 /* scroll a bar in a given direction */
583 scroll (GtkAdjustment* bar, GArray *argv) {
587 gdouble page_size = gtk_adjustment_get_page_size(bar);
588 gdouble value = gtk_adjustment_get_value(bar);
589 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
592 value += page_size * amount * 0.01;
596 max_value = gtk_adjustment_get_upper(bar) - page_size;
598 if (value > max_value)
599 value = max_value; /* don't scroll past the end of the page */
601 gtk_adjustment_set_value (bar, value);
605 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
606 (void) page; (void) argv; (void) result;
607 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
611 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
612 (void) page; (void) argv; (void) result;
613 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
614 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
618 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
619 (void) page; (void) result;
620 scroll(uzbl.gui.bar_v, argv);
624 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
625 (void) page; (void) result;
626 scroll(uzbl.gui.bar_h, argv);
631 if (!uzbl.behave.show_status) {
632 gtk_widget_hide(uzbl.gui.mainbar);
634 gtk_widget_show(uzbl.gui.mainbar);
640 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
645 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
649 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
654 if (uzbl.behave.show_status) {
655 gtk_widget_hide(uzbl.gui.mainbar);
657 gtk_widget_show(uzbl.gui.mainbar);
659 uzbl.behave.show_status = !uzbl.behave.show_status;
664 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
668 //Set selected_url state variable
669 g_free(uzbl.state.selected_url);
670 uzbl.state.selected_url = NULL;
672 uzbl.state.selected_url = g_strdup(link);
678 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
681 const gchar *title = webkit_web_view_get_title(web_view);
682 if (uzbl.gui.main_title)
683 g_free (uzbl.gui.main_title);
684 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
689 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
692 uzbl.gui.sbar.load_progress = progress;
694 g_free(uzbl.gui.sbar.progress_bar);
695 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
701 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
705 if (uzbl.behave.load_finish_handler)
706 run_handler(uzbl.behave.load_finish_handler, "");
709 void clear_keycmd() {
710 g_free(uzbl.state.keycmd);
711 uzbl.state.keycmd = g_strdup("");
715 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
719 uzbl.gui.sbar.load_progress = 0;
720 clear_keycmd(); // don't need old commands to remain on new page?
721 if (uzbl.behave.load_start_handler)
722 run_handler(uzbl.behave.load_start_handler, "");
726 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
729 g_free (uzbl.state.uri);
730 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
731 uzbl.state.uri = g_string_free (newuri, FALSE);
732 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
733 set_insert_mode(uzbl.behave.always_insert_mode);
736 if (uzbl.behave.load_commit_handler)
737 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
741 destroy_cb (GtkWidget* widget, gpointer data) {
749 if (uzbl.behave.history_handler) {
751 struct tm * timeinfo;
754 timeinfo = localtime ( &rawtime );
755 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
756 run_handler(uzbl.behave.history_handler, date);
761 /* VIEW funcs (little webkit wrappers) */
762 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
764 VIEWFUNC(reload_bypass_cache)
765 VIEWFUNC(stop_loading)
772 /* -- command to callback/function map for things we cannot attach to any signals */
773 static struct {char *key; CommandInfo value;} cmdlist[] =
774 { /* key function no_split */
775 { "back", {view_go_back, 0} },
776 { "forward", {view_go_forward, 0} },
777 { "scroll_vert", {scroll_vert, 0} },
778 { "scroll_horz", {scroll_horz, 0} },
779 { "scroll_begin", {scroll_begin, 0} },
780 { "scroll_end", {scroll_end, 0} },
781 { "reload", {view_reload, 0}, },
782 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
783 { "stop", {view_stop_loading, 0}, },
784 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
785 { "zoom_out", {view_zoom_out, 0}, },
786 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
787 { "uri", {load_uri, TRUE} },
788 { "js", {run_js, TRUE} },
789 { "script", {run_external_js, 0} },
790 { "toggle_status", {toggle_status_cb, 0} },
791 { "spawn", {spawn, 0} },
792 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
793 { "sh", {spawn_sh, 0} },
794 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
795 { "exit", {close_uzbl, 0} },
796 { "search", {search_forward_text, TRUE} },
797 { "search_reverse", {search_reverse_text, TRUE} },
798 { "dehilight", {dehilight, 0} },
799 { "toggle_insert_mode", {toggle_insert_mode, 0} },
800 { "set", {set_var, TRUE} },
801 //{ "get", {get_var, TRUE} },
802 { "bind", {act_bind, TRUE} },
803 { "dump_config", {act_dump_config, 0} },
804 { "keycmd", {keycmd, TRUE} },
805 { "keycmd_nl", {keycmd_nl, TRUE} },
806 { "keycmd_bs", {keycmd_bs, 0} },
807 { "chain", {chain, 0} },
808 { "print", {print, TRUE} }
815 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
817 for (i = 0; i < LENGTH(cmdlist); i++)
818 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
821 /* -- CORE FUNCTIONS -- */
824 free_action(gpointer act) {
825 Action *action = (Action*)act;
826 g_free(action->name);
828 g_free(action->param);
833 new_action(const gchar *name, const gchar *param) {
834 Action *action = g_new(Action, 1);
836 action->name = g_strdup(name);
838 action->param = g_strdup(param);
840 action->param = NULL;
846 file_exists (const char * filename) {
847 return (access(filename, F_OK) == 0);
851 set_var(WebKitWebView *page, GArray *argv, GString *result) {
852 (void) page; (void) result;
853 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
854 if (split[0] != NULL) {
855 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
856 set_var_value(g_strstrip(split[0]), value);
863 print(WebKitWebView *page, GArray *argv, GString *result) {
864 (void) page; (void) result;
867 buf = expand(argv_idx(argv, 0), 0);
868 g_string_assign(result, buf);
873 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
874 (void) page; (void) result;
875 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
876 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
877 add_binding(g_strstrip(split[0]), value);
888 void set_mode_indicator() {
889 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
890 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
893 void set_insert_mode(gboolean mode) {
894 uzbl.behave.insert_mode = mode;
895 set_mode_indicator();
899 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
900 (void) page; (void) result;
902 if (argv_idx(argv, 0)) {
903 if (strcmp (argv_idx(argv, 0), "0") == 0) {
904 set_insert_mode(FALSE);
906 set_insert_mode(TRUE);
909 set_insert_mode( !uzbl.behave.insert_mode );
916 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
919 if (argv_idx(argv, 0)) {
920 GString* newuri = g_string_new (argv_idx(argv, 0));
921 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
922 run_js(web_view, argv, NULL);
925 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
926 g_string_prepend (newuri, "http://");
927 /* if we do handle cookies, ask our handler for them */
928 webkit_web_view_load_uri (web_view, newuri->str);
929 g_string_free (newuri, TRUE);
937 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
938 size_t argumentCount, const JSValueRef arguments[],
939 JSValueRef* exception) {
944 JSStringRef js_result_string;
945 GString *result = g_string_new("");
947 if (argumentCount >= 1) {
948 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
949 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
950 char ctl_line[arg_size];
951 JSStringGetUTF8CString(arg, ctl_line, arg_size);
953 parse_cmd_line(ctl_line, result);
955 JSStringRelease(arg);
957 js_result_string = JSStringCreateWithUTF8CString(result->str);
959 g_string_free(result, TRUE);
961 return JSValueMakeString(ctx, js_result_string);
964 static JSStaticFunction js_static_functions[] = {
965 {"run", js_run_command, kJSPropertyAttributeNone},
970 /* This function creates the class and its definition, only once */
971 if (!uzbl.js.initialized) {
972 /* it would be pretty cool to make this dynamic */
973 uzbl.js.classdef = kJSClassDefinitionEmpty;
974 uzbl.js.classdef.staticFunctions = js_static_functions;
976 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
982 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
983 WebKitWebFrame *frame;
984 JSGlobalContextRef context;
985 JSObjectRef globalobject;
986 JSStringRef var_name;
988 JSStringRef js_script;
989 JSValueRef js_result;
990 JSStringRef js_result_string;
991 size_t js_result_size;
995 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
996 context = webkit_web_frame_get_global_context(frame);
997 globalobject = JSContextGetGlobalObject(context);
999 /* uzbl javascript namespace */
1000 var_name = JSStringCreateWithUTF8CString("Uzbl");
1001 JSObjectSetProperty(context, globalobject, var_name,
1002 JSObjectMake(context, uzbl.js.classref, NULL),
1003 kJSClassAttributeNone, NULL);
1005 /* evaluate the script and get return value*/
1006 js_script = JSStringCreateWithUTF8CString(script);
1007 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1008 if (js_result && !JSValueIsUndefined(context, js_result)) {
1009 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1010 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1012 if (js_result_size) {
1013 char js_result_utf8[js_result_size];
1014 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1015 g_string_assign(result, js_result_utf8);
1018 JSStringRelease(js_result_string);
1022 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1024 JSStringRelease(var_name);
1025 JSStringRelease(js_script);
1029 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1031 if (argv_idx(argv, 0))
1032 eval_js(web_view, argv_idx(argv, 0), result);
1036 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1038 if (argv_idx(argv, 0)) {
1039 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1044 while ((line = g_array_index(lines, gchar*, i))) {
1046 js = g_strdup (line);
1048 gchar* newjs = g_strconcat (js, line, NULL);
1055 if (uzbl.state.verbose)
1056 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1058 if (argv_idx (argv, 1)) {
1059 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1063 eval_js (web_view, js, result);
1065 g_array_free (lines, TRUE);
1070 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1071 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1072 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1073 webkit_web_view_unmark_text_matches (page);
1074 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1075 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1079 if (uzbl.state.searchtx) {
1080 if (uzbl.state.verbose)
1081 printf ("Searching: %s\n", uzbl.state.searchtx);
1082 webkit_web_view_set_highlight_text_matches (page, TRUE);
1083 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1088 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1090 search_text(page, argv, TRUE);
1094 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1096 search_text(page, argv, FALSE);
1100 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1101 (void) argv; (void) result;
1102 webkit_web_view_set_highlight_text_matches (page, FALSE);
1107 new_window_load_uri (const gchar * uri) {
1108 if (uzbl.behave.new_window) {
1109 GString *s = g_string_new ("");
1110 g_string_printf(s, "'%s'", uri);
1111 run_handler(uzbl.behave.new_window, s->str);
1114 GString* to_execute = g_string_new ("");
1115 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1117 for (i = 0; entries[i].long_name != NULL; i++) {
1118 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1119 gchar** str = (gchar**)entries[i].arg_data;
1121 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1125 if (uzbl.state.verbose)
1126 printf("\n%s\n", to_execute->str);
1127 g_spawn_command_line_async (to_execute->str, NULL);
1128 g_string_free (to_execute, TRUE);
1132 chain (WebKitWebView *page, GArray *argv, GString *result) {
1133 (void) page; (void) result;
1135 gchar **parts = NULL;
1137 while ((a = argv_idx(argv, i++))) {
1138 parts = g_strsplit (a, " ", 2);
1139 parse_command(parts[0], parts[1], result);
1145 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1149 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1155 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1159 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1165 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1170 int len = strlen(uzbl.state.keycmd);
1171 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1173 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1178 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1185 /* --Statusbar functions-- */
1187 build_progressbar_ascii(int percent) {
1188 int width=uzbl.gui.sbar.progress_w;
1191 GString *bar = g_string_new("");
1193 l = (double)percent*((double)width/100.);
1194 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1196 for(i=0; i<(int)l; i++)
1197 g_string_append(bar, uzbl.gui.sbar.progress_s);
1200 g_string_append(bar, uzbl.gui.sbar.progress_u);
1202 return g_string_free(bar, FALSE);
1204 /* --End Statusbar functions-- */
1207 sharg_append(GArray *a, const gchar *str) {
1208 const gchar *s = (str ? str : "");
1209 g_array_append_val(a, s);
1212 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1214 run_command (const gchar *command, const guint npre, const gchar **args,
1215 const gboolean sync, char **output_stdout) {
1216 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1219 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1220 gchar *pid = itos(getpid());
1221 gchar *xwin = itos(uzbl.xwin);
1223 sharg_append(a, command);
1224 for (i = 0; i < npre; i++) /* add n args before the default vars */
1225 sharg_append(a, args[i]);
1226 sharg_append(a, uzbl.state.config_file);
1227 sharg_append(a, pid);
1228 sharg_append(a, xwin);
1229 sharg_append(a, uzbl.comm.fifo_path);
1230 sharg_append(a, uzbl.comm.socket_path);
1231 sharg_append(a, uzbl.state.uri);
1232 sharg_append(a, uzbl.gui.main_title);
1234 for (i = npre; i < g_strv_length((gchar**)args); i++)
1235 sharg_append(a, args[i]);
1239 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1241 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1242 NULL, NULL, output_stdout, NULL, NULL, &err);
1243 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1244 NULL, NULL, NULL, &err);
1246 if (uzbl.state.verbose) {
1247 GString *s = g_string_new("spawned:");
1248 for (i = 0; i < (a->len); i++) {
1249 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1250 g_string_append_printf(s, " %s", qarg);
1253 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1254 printf("%s\n", s->str);
1255 g_string_free(s, TRUE);
1257 printf("Stdout: %s\n", *output_stdout);
1261 g_printerr("error on run_command: %s\n", err->message);
1266 g_array_free (a, TRUE);
1271 split_quoted(const gchar* src, const gboolean unquote) {
1272 /* split on unquoted space, return array of strings;
1273 remove a layer of quotes and backslashes if unquote */
1274 if (!src) return NULL;
1276 gboolean dq = FALSE;
1277 gboolean sq = FALSE;
1278 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1279 GString *s = g_string_new ("");
1283 for (p = src; *p != '\0'; p++) {
1284 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1285 else if (*p == '\\') { g_string_append_c(s, *p++);
1286 g_string_append_c(s, *p); }
1287 else if ((*p == '"') && unquote && !sq) dq = !dq;
1288 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1290 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1291 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1293 else if ((*p == ' ') && !dq && !sq) {
1294 dup = g_strdup(s->str);
1295 g_array_append_val(a, dup);
1296 g_string_truncate(s, 0);
1297 } else g_string_append_c(s, *p);
1299 dup = g_strdup(s->str);
1300 g_array_append_val(a, dup);
1301 ret = (gchar**)a->data;
1302 g_array_free (a, FALSE);
1303 g_string_free (s, TRUE);
1308 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1309 (void)web_view; (void)result;
1310 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1311 if (argv_idx(argv, 0))
1312 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1316 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1317 (void)web_view; (void)result;
1319 if (argv_idx(argv, 0))
1320 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1321 TRUE, &uzbl.comm.sync_stdout);
1325 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1326 (void)web_view; (void)result;
1327 if (!uzbl.behave.shell_cmd) {
1328 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1333 gchar *spacer = g_strdup("");
1334 g_array_insert_val(argv, 1, spacer);
1335 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1337 for (i = 1; i < g_strv_length(cmd); i++)
1338 g_array_prepend_val(argv, cmd[i]);
1340 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1346 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1347 (void)web_view; (void)result;
1348 if (!uzbl.behave.shell_cmd) {
1349 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1354 gchar *spacer = g_strdup("");
1355 g_array_insert_val(argv, 1, spacer);
1356 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1358 for (i = 1; i < g_strv_length(cmd); i++)
1359 g_array_prepend_val(argv, cmd[i]);
1361 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1362 TRUE, &uzbl.comm.sync_stdout);
1368 parse_command(const char *cmd, const char *param, GString *result) {
1371 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1373 gchar **par = split_quoted(param, TRUE);
1374 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1376 if (c->no_split) { /* don't split */
1377 sharg_append(a, param);
1379 for (i = 0; i < g_strv_length(par); i++)
1380 sharg_append(a, par[i]);
1383 if (result == NULL) {
1384 GString *result_print = g_string_new("");
1386 c->function(uzbl.gui.web_view, a, result_print);
1387 if (result_print->len)
1388 printf("%*s\n", result_print->len, result_print->str);
1390 g_string_free(result_print, TRUE);
1392 c->function(uzbl.gui.web_view, a, result);
1395 g_array_free (a, TRUE);
1398 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1405 if(*uzbl.net.proxy_url == ' '
1406 || uzbl.net.proxy_url == NULL) {
1407 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1408 (GType) SOUP_SESSION_PROXY_URI);
1411 suri = soup_uri_new(uzbl.net.proxy_url);
1412 g_object_set(G_OBJECT(uzbl.net.soup_session),
1413 SOUP_SESSION_PROXY_URI,
1415 soup_uri_free(suri);
1422 if(file_exists(uzbl.gui.icon)) {
1423 if (uzbl.gui.main_window)
1424 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1426 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1432 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1433 g_array_append_val (a, uzbl.state.uri);
1434 load_uri(uzbl.gui.web_view, a, NULL);
1435 g_array_free (a, TRUE);
1439 cmd_always_insert_mode() {
1440 set_insert_mode(uzbl.behave.always_insert_mode);
1446 g_object_set(G_OBJECT(uzbl.net.soup_session),
1447 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1451 cmd_max_conns_host() {
1452 g_object_set(G_OBJECT(uzbl.net.soup_session),
1453 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1458 soup_session_remove_feature
1459 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1460 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1461 /*g_free(uzbl.net.soup_logger);*/
1463 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1464 soup_session_add_feature(uzbl.net.soup_session,
1465 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1468 static WebKitWebSettings*
1470 return webkit_web_view_get_settings(uzbl.gui.web_view);
1475 WebKitWebSettings *ws = view_settings();
1476 if (uzbl.behave.font_size > 0) {
1477 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1480 if (uzbl.behave.monospace_size > 0) {
1481 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1482 uzbl.behave.monospace_size, NULL);
1484 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1485 uzbl.behave.font_size, NULL);
1491 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1495 cmd_disable_plugins() {
1496 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1497 !uzbl.behave.disable_plugins, NULL);
1501 cmd_disable_scripts() {
1502 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1503 !uzbl.behave.disable_scripts, NULL);
1507 cmd_minimum_font_size() {
1508 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1509 uzbl.behave.minimum_font_size, NULL);
1512 cmd_autoload_img() {
1513 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1514 uzbl.behave.autoload_img, NULL);
1519 cmd_autoshrink_img() {
1520 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1521 uzbl.behave.autoshrink_img, NULL);
1526 cmd_enable_spellcheck() {
1527 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1528 uzbl.behave.enable_spellcheck, NULL);
1532 cmd_enable_private() {
1533 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1534 uzbl.behave.enable_private, NULL);
1539 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1540 uzbl.behave.print_bg, NULL);
1545 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1546 uzbl.behave.style_uri, NULL);
1550 cmd_resizable_txt() {
1551 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1552 uzbl.behave.resizable_txt, NULL);
1556 cmd_default_encoding() {
1557 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1558 uzbl.behave.default_encoding, NULL);
1562 cmd_enforce_96dpi() {
1563 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1564 uzbl.behave.enforce_96dpi, NULL);
1568 cmd_caret_browsing() {
1569 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1570 uzbl.behave.caret_browsing, NULL);
1574 cmd_cookie_handler() {
1575 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1576 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1577 if ((g_strcmp0(split[0], "sh") == 0) ||
1578 (g_strcmp0(split[0], "spawn") == 0)) {
1579 g_free (uzbl.behave.cookie_handler);
1580 uzbl.behave.cookie_handler =
1581 g_strdup_printf("sync_%s %s", split[0], split[1]);
1588 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1589 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1590 if ((g_strcmp0(split[0], "sh") == 0) ||
1591 (g_strcmp0(split[0], "spawn") == 0)) {
1592 g_free (uzbl.behave.new_window);
1593 uzbl.behave.new_window =
1594 g_strdup_printf("%s %s", split[0], split[1]);
1601 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1606 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1611 if(uzbl.behave.inject_html) {
1612 webkit_web_view_load_html_string (uzbl.gui.web_view,
1613 uzbl.behave.inject_html, NULL);
1622 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1623 uzbl.behave.modmask = 0;
1625 if(uzbl.behave.modkey)
1626 g_free(uzbl.behave.modkey);
1627 uzbl.behave.modkey = buf;
1629 for (i = 0; modkeys[i].key != NULL; i++) {
1630 if (g_strrstr(buf, modkeys[i].key))
1631 uzbl.behave.modmask |= modkeys[i].mask;
1637 if (*uzbl.net.useragent == ' ') {
1638 g_free (uzbl.net.useragent);
1639 uzbl.net.useragent = NULL;
1641 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1642 uzbl.net.useragent, NULL);
1648 gtk_widget_ref(uzbl.gui.scrolled_win);
1649 gtk_widget_ref(uzbl.gui.mainbar);
1650 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1651 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1653 if(uzbl.behave.status_top) {
1654 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1655 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1658 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1659 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1661 gtk_widget_unref(uzbl.gui.scrolled_win);
1662 gtk_widget_unref(uzbl.gui.mainbar);
1663 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1668 set_var_value(gchar *name, gchar *val) {
1669 uzbl_cmdprop *c = NULL;
1673 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1674 if(!c->writeable) return TRUE;
1676 /* check for the variable type */
1677 if (c->type == TYPE_STR) {
1678 buf = expand(val, 0);
1681 } else if(c->type == TYPE_INT) {
1682 int *ip = (int *)c->ptr;
1683 buf = expand(val, 0);
1684 *ip = (int)strtoul(buf, &endp, 10);
1686 } else if (c->type == TYPE_FLOAT) {
1687 float *fp = (float *)c->ptr;
1688 buf = expand(val, 0);
1689 *fp = strtod(buf, &endp);
1693 /* invoke a command specific function */
1694 if(c->func) c->func();
1701 Behaviour *b = &uzbl.behave;
1703 if(b->html_buffer->str) {
1704 webkit_web_view_load_html_string (uzbl.gui.web_view,
1705 b->html_buffer->str, b->base_url);
1706 g_string_free(b->html_buffer, TRUE);
1707 b->html_buffer = g_string_new("");
1711 enum {M_CMD, M_HTML};
1713 parse_cmd_line(const char *ctl_line, GString *result) {
1714 Behaviour *b = &uzbl.behave;
1717 if(b->mode == M_HTML) {
1718 len = strlen(b->html_endmarker);
1719 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1720 if(len == strlen(ctl_line)-1 &&
1721 !strncmp(b->html_endmarker, ctl_line, len)) {
1723 set_var_value("mode", "0");
1728 set_timeout(b->html_timeout);
1729 g_string_append(b->html_buffer, ctl_line);
1732 else if((ctl_line[0] == '#') /* Comments */
1733 || (ctl_line[0] == ' ')
1734 || (ctl_line[0] == '\n'))
1735 ; /* ignore these lines */
1736 else { /* parse a command */
1738 gchar **tokens = NULL;
1739 len = strlen(ctl_line);
1741 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1742 ctlstrip = g_strndup(ctl_line, len - 1);
1743 else ctlstrip = g_strdup(ctl_line);
1745 tokens = g_strsplit(ctlstrip, " ", 2);
1746 parse_command(tokens[0], tokens[1], result);
1753 build_stream_name(int type, const gchar* dir) {
1754 State *s = &uzbl.state;
1758 str = g_strdup_printf
1759 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1760 } else if (type == SOCKET) {
1761 str = g_strdup_printf
1762 ("%s/uzbl_socket_%s", dir, s->instance_name);
1768 control_fifo(GIOChannel *gio, GIOCondition condition) {
1769 if (uzbl.state.verbose)
1770 printf("triggered\n");
1775 if (condition & G_IO_HUP)
1776 g_error ("Fifo: Read end of pipe died!\n");
1779 g_error ("Fifo: GIOChannel broke\n");
1781 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1782 if (ret == G_IO_STATUS_ERROR) {
1783 g_error ("Fifo: Error reading: %s\n", err->message);
1787 parse_cmd_line(ctl_line, NULL);
1794 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1795 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1796 if (unlink(uzbl.comm.fifo_path) == -1)
1797 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1798 g_free(uzbl.comm.fifo_path);
1799 uzbl.comm.fifo_path = NULL;
1802 if (*dir == ' ') { /* space unsets the variable */
1807 GIOChannel *chan = NULL;
1808 GError *error = NULL;
1809 gchar *path = build_stream_name(FIFO, dir);
1811 if (!file_exists(path)) {
1812 if (mkfifo (path, 0666) == 0) {
1813 // 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.
1814 chan = g_io_channel_new_file(path, "r+", &error);
1816 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1817 if (uzbl.state.verbose)
1818 printf ("init_fifo: created successfully as %s\n", path);
1819 uzbl.comm.fifo_path = path;
1821 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1822 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1823 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1824 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1826 /* if we got this far, there was an error; cleanup */
1827 if (error) g_error_free (error);
1834 control_stdin(GIOChannel *gio, GIOCondition condition) {
1836 gchar *ctl_line = NULL;
1839 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1840 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1843 parse_cmd_line(ctl_line, NULL);
1851 GIOChannel *chan = NULL;
1852 GError *error = NULL;
1854 chan = g_io_channel_unix_new(fileno(stdin));
1856 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1857 g_error ("Stdin: could not add watch\n");
1859 if (uzbl.state.verbose)
1860 printf ("Stdin: watch added successfully\n");
1863 g_error ("Stdin: Error while opening: %s\n", error->message);
1865 if (error) g_error_free (error);
1869 control_socket(GIOChannel *chan) {
1870 struct sockaddr_un remote;
1871 unsigned int t = sizeof(remote);
1873 GIOChannel *clientchan;
1875 clientsock = accept (g_io_channel_unix_get_fd(chan),
1876 (struct sockaddr *) &remote, &t);
1878 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1879 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1880 (GIOFunc) control_client_socket, clientchan);
1887 control_client_socket(GIOChannel *clientchan) {
1889 GString *result = g_string_new("");
1890 GError *error = NULL;
1894 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1895 if (ret == G_IO_STATUS_ERROR) {
1896 g_warning ("Error reading: %s\n", error->message);
1897 g_io_channel_shutdown(clientchan, TRUE, &error);
1899 } else if (ret == G_IO_STATUS_EOF) {
1900 /* shutdown and remove channel watch from main loop */
1901 g_io_channel_shutdown(clientchan, TRUE, &error);
1906 parse_cmd_line (ctl_line, result);
1907 g_string_append_c(result, '\n');
1908 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1910 if (ret == G_IO_STATUS_ERROR) {
1911 g_warning ("Error writing: %s", error->message);
1913 g_io_channel_flush(clientchan, &error);
1916 if (error) g_error_free (error);
1917 g_string_free(result, TRUE);
1923 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1924 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1925 if (unlink(uzbl.comm.socket_path) == -1)
1926 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1927 g_free(uzbl.comm.socket_path);
1928 uzbl.comm.socket_path = NULL;
1936 GIOChannel *chan = NULL;
1938 struct sockaddr_un local;
1939 gchar *path = build_stream_name(SOCKET, dir);
1941 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1943 local.sun_family = AF_UNIX;
1944 strcpy (local.sun_path, path);
1945 unlink (local.sun_path);
1947 len = strlen (local.sun_path) + sizeof (local.sun_family);
1948 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1949 if (uzbl.state.verbose)
1950 printf ("init_socket: opened in %s\n", path);
1953 if( (chan = g_io_channel_unix_new(sock)) ) {
1954 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1955 uzbl.comm.socket_path = path;
1958 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1960 /* if we got this far, there was an error; cleanup */
1967 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1968 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1970 // this function may be called very early when the templates are not set (yet), hence the checks
1972 update_title (void) {
1973 Behaviour *b = &uzbl.behave;
1976 if (b->show_status) {
1977 if (b->title_format_short) {
1978 parsed = expand(b->title_format_short, 0);
1979 if (uzbl.gui.main_window)
1980 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1983 if (b->status_format) {
1984 parsed = expand(b->status_format, 0);
1985 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1988 if (b->status_background) {
1990 gdk_color_parse (b->status_background, &color);
1991 //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
1992 if (uzbl.gui.main_window)
1993 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1994 else if (uzbl.gui.plug)
1995 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
1998 if (b->title_format_long) {
1999 parsed = expand(b->title_format_long, 0);
2000 if (uzbl.gui.main_window)
2001 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2008 key_press_cb (GtkWidget* window, GdkEventKey* event)
2010 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2014 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2015 || 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)
2018 /* turn off insert mode (if always_insert_mode is not used) */
2019 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2020 set_insert_mode(uzbl.behave.always_insert_mode);
2025 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2028 if (event->keyval == GDK_Escape) {
2031 dehilight(uzbl.gui.web_view, NULL, NULL);
2035 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2036 if (event->keyval == GDK_Insert) {
2038 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2039 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2041 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2044 GString* keycmd = g_string_new(uzbl.state.keycmd);
2045 g_string_append (keycmd, str);
2046 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2053 if (event->keyval == GDK_BackSpace)
2054 keycmd_bs(NULL, NULL, NULL);
2056 gboolean key_ret = FALSE;
2057 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2060 GString* keycmd = g_string_new(uzbl.state.keycmd);
2061 g_string_append(keycmd, event->string);
2062 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2065 run_keycmd(key_ret);
2067 if (key_ret) return (!uzbl.behave.insert_mode);
2072 run_keycmd(const gboolean key_ret) {
2073 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2075 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2077 parse_command(act->name, act->param, NULL);
2081 /* try if it's an incremental keycmd or one that takes args, and run it */
2082 GString* short_keys = g_string_new ("");
2083 GString* short_keys_inc = g_string_new ("");
2085 guint len = strlen(uzbl.state.keycmd);
2086 for (i=0; i<len; i++) {
2087 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2088 g_string_assign(short_keys_inc, short_keys->str);
2089 g_string_append_c(short_keys, '_');
2090 g_string_append_c(short_keys_inc, '*');
2092 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2093 /* run normal cmds only if return was pressed */
2094 exec_paramcmd(act, i);
2097 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2098 if (key_ret) /* just quit the incremental command on return */
2100 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2104 g_string_truncate(short_keys, short_keys->len - 1);
2106 g_string_free (short_keys, TRUE);
2107 g_string_free (short_keys_inc, TRUE);
2111 exec_paramcmd(const Action *act, const guint i) {
2112 GString *parampart = g_string_new (uzbl.state.keycmd);
2113 GString *actionname = g_string_new ("");
2114 GString *actionparam = g_string_new ("");
2115 g_string_erase (parampart, 0, i+1);
2117 g_string_printf (actionname, act->name, parampart->str);
2119 g_string_printf (actionparam, act->param, parampart->str);
2120 parse_command(actionname->str, actionparam->str, NULL);
2121 g_string_free(actionname, TRUE);
2122 g_string_free(actionparam, TRUE);
2123 g_string_free(parampart, TRUE);
2131 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2132 //main_window_ref = g_object_ref(scrolled_window);
2133 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
2135 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2136 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2138 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2139 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2140 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2141 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2142 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2143 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2144 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2145 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2146 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2147 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2148 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2150 return scrolled_window;
2157 g->mainbar = gtk_hbox_new (FALSE, 0);
2159 /* keep a reference to the bar so we can re-pack it at runtime*/
2160 //sbar_ref = g_object_ref(g->mainbar);
2162 g->mainbar_label = gtk_label_new ("");
2163 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2164 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2165 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2166 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2167 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2168 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2173 GtkWidget* create_window () {
2174 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2175 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2176 gtk_widget_set_name (window, "Uzbl browser");
2177 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2178 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2184 GtkPlug* create_plug () {
2185 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2186 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2187 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2194 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2196 If actname is one that calls an external command, this function will inject
2197 newargs in front of the user-provided args in that command line. They will
2198 come become after the body of the script (in sh) or after the name of
2199 the command to execute (in spawn).
2200 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2201 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2203 The return value consist of two strings: the action (sh, ...) and its args.
2205 If act is not one that calls an external command, then the given action merely
2208 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2209 gchar *actdup = g_strdup(actname);
2210 g_array_append_val(rets, actdup);
2212 if ((g_strcmp0(actname, "spawn") == 0) ||
2213 (g_strcmp0(actname, "sh") == 0) ||
2214 (g_strcmp0(actname, "sync_spawn") == 0) ||
2215 (g_strcmp0(actname, "sync_sh") == 0)) {
2217 GString *a = g_string_new("");
2218 gchar **spawnparts = split_quoted(origargs, FALSE);
2219 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2220 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2222 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2223 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2225 g_array_append_val(rets, a->str);
2226 g_string_free(a, FALSE);
2227 g_strfreev(spawnparts);
2229 gchar *origdup = g_strdup(origargs);
2230 g_array_append_val(rets, origdup);
2232 return (gchar**)g_array_free(rets, FALSE);
2236 run_handler (const gchar *act, const gchar *args) {
2237 /* Consider this code a temporary hack to make the handlers usable.
2238 In practice, all this splicing, injection, and reconstruction is
2239 inefficient, annoying and hard to manage. Potential pitfalls arise
2240 when the handler specific args 1) are not quoted (the handler
2241 callbacks should take care of this) 2) are quoted but interfere
2242 with the users' own quotation. A more ideal solution is
2243 to refactor parse_command so that it doesn't just take a string
2244 and execute it; rather than that, we should have a function which
2245 returns the argument vector parsed from the string. This vector
2246 could be modified (e.g. insert additional args into it) before
2247 passing it to the next function that actually executes it. Though
2248 it still isn't perfect for chain actions.. will reconsider & re-
2249 factor when I have the time. -duc */
2251 char **parts = g_strsplit(act, " ", 2);
2253 if (g_strcmp0(parts[0], "chain") == 0) {
2254 GString *newargs = g_string_new("");
2255 gchar **chainparts = split_quoted(parts[1], FALSE);
2257 /* for every argument in the chain, inject the handler args
2258 and make sure the new parts are wrapped in quotes */
2259 gchar **cp = chainparts;
2261 gchar *quotless = NULL;
2262 gchar **spliced_quotless = NULL; // sigh -_-;
2263 gchar **inpart = NULL;
2266 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2268 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2269 } else quotless = g_strdup(*cp);
2271 spliced_quotless = g_strsplit(quotless, " ", 2);
2272 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2273 g_strfreev(spliced_quotless);
2275 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2281 parse_command(parts[0], &(newargs->str[1]), NULL);
2282 g_string_free(newargs, TRUE);
2283 g_strfreev(chainparts);
2286 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2287 parse_command(inparts[0], inparts[1], NULL);
2295 add_binding (const gchar *key, const gchar *act) {
2296 char **parts = g_strsplit(act, " ", 2);
2303 if (uzbl.state.verbose)
2304 printf ("Binding %-10s : %s\n", key, act);
2305 action = new_action(parts[0], parts[1]);
2307 if (g_hash_table_remove (uzbl.bindings, key))
2308 g_warning ("Overwriting existing binding for \"%s\"", key);
2309 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2314 get_xdg_var (XDG_Var xdg) {
2315 const gchar* actual_value = getenv (xdg.environmental);
2316 const gchar* home = getenv ("HOME");
2317 gchar* return_value;
2319 if (! actual_value || strcmp (actual_value, "") == 0) {
2320 if (xdg.default_value) {
2321 return_value = str_replace ("~", home, xdg.default_value);
2323 return_value = NULL;
2326 return_value = str_replace("~", home, actual_value);
2329 return return_value;
2333 find_xdg_file (int xdg_type, char* filename) {
2334 /* xdg_type = 0 => config
2335 xdg_type = 1 => data
2336 xdg_type = 2 => cache*/
2338 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2339 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2342 gchar* temporary_string;
2346 if (! file_exists (temporary_file) && xdg_type != 2) {
2347 buf = get_xdg_var (XDG[3 + xdg_type]);
2348 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2351 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2352 g_free (temporary_file);
2353 temporary_file = g_strconcat (temporary_string, filename, NULL);
2357 //g_free (temporary_string); - segfaults.
2359 if (file_exists (temporary_file)) {
2360 return temporary_file;
2367 State *s = &uzbl.state;
2368 Network *n = &uzbl.net;
2370 for (i = 0; default_config[i].command != NULL; i++) {
2371 parse_cmd_line(default_config[i].command, NULL);
2374 if (g_strcmp0(s->config_file, "-") == 0) {
2375 s->config_file = NULL;
2379 else if (!s->config_file) {
2380 s->config_file = find_xdg_file (0, "/uzbl/config");
2383 if (s->config_file) {
2384 GArray* lines = read_file_by_line (s->config_file);
2388 while ((line = g_array_index(lines, gchar*, i))) {
2389 parse_cmd_line (line, NULL);
2393 g_array_free (lines, TRUE);
2395 if (uzbl.state.verbose)
2396 printf ("No configuration file loaded.\n");
2399 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2402 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2405 if (!uzbl.behave.cookie_handler)
2408 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2409 GString *s = g_string_new ("");
2410 SoupURI * soup_uri = soup_message_get_uri(msg);
2411 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2412 run_handler(uzbl.behave.cookie_handler, s->str);
2414 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2415 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2416 if ( p != NULL ) *p = '\0';
2417 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2419 if (uzbl.comm.sync_stdout)
2420 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2422 g_string_free(s, TRUE);
2426 save_cookies (SoupMessage *msg, gpointer user_data){
2430 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2431 cookie = soup_cookie_to_set_cookie_header(ck->data);
2432 SoupURI * soup_uri = soup_message_get_uri(msg);
2433 GString *s = g_string_new ("");
2434 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2435 run_handler(uzbl.behave.cookie_handler, s->str);
2437 g_string_free(s, TRUE);
2442 /* --- WEBINSPECTOR --- */
2444 hide_window_cb(GtkWidget *widget, gpointer data) {
2447 gtk_widget_hide(widget);
2450 static WebKitWebView*
2451 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2454 (void) web_inspector;
2455 GtkWidget* scrolled_window;
2456 GtkWidget* new_web_view;
2459 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2460 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2461 G_CALLBACK(hide_window_cb), NULL);
2463 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2464 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2465 gtk_widget_show(g->inspector_window);
2467 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2468 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2469 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2470 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2471 gtk_widget_show(scrolled_window);
2473 new_web_view = webkit_web_view_new();
2474 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2476 return WEBKIT_WEB_VIEW(new_web_view);
2480 inspector_show_window_cb (WebKitWebInspector* inspector){
2482 gtk_widget_show(uzbl.gui.inspector_window);
2486 /* TODO: Add variables and code to make use of these functions */
2488 inspector_close_window_cb (WebKitWebInspector* inspector){
2494 inspector_attach_window_cb (WebKitWebInspector* inspector){
2500 inspector_detach_window_cb (WebKitWebInspector* inspector){
2506 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2512 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2518 set_up_inspector() {
2520 WebKitWebSettings *settings = view_settings();
2521 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2523 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2524 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2525 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2526 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2527 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2528 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2529 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2531 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2535 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2537 uzbl_cmdprop *c = v;
2542 if(c->type == TYPE_STR)
2543 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2544 else if(c->type == TYPE_INT)
2545 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2549 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2553 printf("bind %s = %s %s\n", (char *)k ,
2554 (char *)a->name, a->param?(char *)a->param:"");
2559 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2560 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2563 /* set up gtk, gobject, variable defaults and other things that tests and other
2564 * external applications need to do anyhow */
2566 initialize(int argc, char *argv[]) {
2567 gtk_init (&argc, &argv);
2568 if (!g_thread_supported ())
2569 g_thread_init (NULL);
2570 uzbl.state.executable_path = g_strdup(argv[0]);
2571 uzbl.state.selected_url = NULL;
2572 uzbl.state.searchtx = NULL;
2574 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2575 g_option_context_add_main_entries (context, entries, NULL);
2576 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2577 g_option_context_parse (context, &argc, &argv, NULL);
2578 g_option_context_free(context);
2580 if (uzbl.behave.print_version) {
2581 printf("Commit: %s\n", COMMIT);
2585 /* initialize hash table */
2586 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2588 uzbl.net.soup_session = webkit_get_default_session();
2589 uzbl.state.keycmd = g_strdup("");
2591 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2592 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2593 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2594 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2595 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2596 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2598 uzbl.gui.sbar.progress_s = g_strdup("=");
2599 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2600 uzbl.gui.sbar.progress_w = 10;
2602 /* HTML mode defaults*/
2603 uzbl.behave.html_buffer = g_string_new("");
2604 uzbl.behave.html_endmarker = g_strdup(".");
2605 uzbl.behave.html_timeout = 60;
2606 uzbl.behave.base_url = g_strdup("http://invalid");
2608 /* default mode indicators */
2609 uzbl.behave.insert_indicator = g_strdup("I");
2610 uzbl.behave.cmd_indicator = g_strdup("C");
2611 set_insert_mode(FALSE);
2613 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2614 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2615 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2616 uzbl.info.arch = ARCH;
2617 uzbl.info.commit = COMMIT;
2620 make_var_to_name_hash();
2622 uzbl.gui.scrolled_win = create_browser();
2625 #ifndef UZBL_LIBRARY
2628 main (int argc, char* argv[]) {
2629 initialize(argc, argv);
2631 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2635 /* initial packing */
2636 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2637 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2639 if (uzbl.state.socket_id) {
2640 uzbl.gui.plug = create_plug ();
2641 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2642 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2644 uzbl.gui.main_window = create_window ();
2645 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2646 gtk_widget_show_all (uzbl.gui.main_window);
2647 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2650 if(!uzbl.state.instance_name)
2651 uzbl.state.instance_name = itos((int)uzbl.xwin);
2653 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2655 if (uzbl.state.verbose) {
2656 printf("Uzbl start location: %s\n", argv[0]);
2657 if (uzbl.state.socket_id)
2658 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2660 printf("window_id %i\n",(int) uzbl.xwin);
2661 printf("pid %i\n", getpid ());
2662 printf("name: %s\n", uzbl.state.instance_name);
2665 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2666 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2667 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2668 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2669 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2671 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2672 gboolean verbose_override = uzbl.state.verbose;
2676 if (!uzbl.behave.show_status)
2677 gtk_widget_hide(uzbl.gui.mainbar);
2684 if (verbose_override > uzbl.state.verbose)
2685 uzbl.state.verbose = verbose_override;
2688 set_var_value("uri", uri_override);
2689 g_free(uri_override);
2690 } else if (uzbl.state.uri)
2691 cmd_load_uri(uzbl.gui.web_view, NULL);
2696 return EXIT_SUCCESS;
2700 /* vi: set et ts=4: */