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, set_keycmd)},
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);
893 void set_mode_indicator() {
894 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
895 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
898 void set_insert_mode(gboolean mode) {
899 uzbl.behave.insert_mode = mode;
900 set_mode_indicator();
904 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
905 (void) page; (void) result;
907 if (argv_idx(argv, 0)) {
908 if (strcmp (argv_idx(argv, 0), "0") == 0) {
909 set_insert_mode(FALSE);
911 set_insert_mode(TRUE);
914 set_insert_mode( !uzbl.behave.insert_mode );
921 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
924 if (argv_idx(argv, 0)) {
925 GString* newuri = g_string_new (argv_idx(argv, 0));
926 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
927 run_js(web_view, argv, NULL);
930 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
931 g_string_prepend (newuri, "http://");
932 /* if we do handle cookies, ask our handler for them */
933 webkit_web_view_load_uri (web_view, newuri->str);
934 g_string_free (newuri, TRUE);
942 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
943 size_t argumentCount, const JSValueRef arguments[],
944 JSValueRef* exception) {
949 JSStringRef js_result_string;
950 GString *result = g_string_new("");
952 if (argumentCount >= 1) {
953 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
954 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
955 char ctl_line[arg_size];
956 JSStringGetUTF8CString(arg, ctl_line, arg_size);
958 parse_cmd_line(ctl_line, result);
960 JSStringRelease(arg);
962 js_result_string = JSStringCreateWithUTF8CString(result->str);
964 g_string_free(result, TRUE);
966 return JSValueMakeString(ctx, js_result_string);
969 static JSStaticFunction js_static_functions[] = {
970 {"run", js_run_command, kJSPropertyAttributeNone},
975 /* This function creates the class and its definition, only once */
976 if (!uzbl.js.initialized) {
977 /* it would be pretty cool to make this dynamic */
978 uzbl.js.classdef = kJSClassDefinitionEmpty;
979 uzbl.js.classdef.staticFunctions = js_static_functions;
981 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
987 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
988 WebKitWebFrame *frame;
989 JSGlobalContextRef context;
990 JSObjectRef globalobject;
991 JSStringRef var_name;
993 JSStringRef js_script;
994 JSValueRef js_result;
995 JSStringRef js_result_string;
996 size_t js_result_size;
1000 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1001 context = webkit_web_frame_get_global_context(frame);
1002 globalobject = JSContextGetGlobalObject(context);
1004 /* uzbl javascript namespace */
1005 var_name = JSStringCreateWithUTF8CString("Uzbl");
1006 JSObjectSetProperty(context, globalobject, var_name,
1007 JSObjectMake(context, uzbl.js.classref, NULL),
1008 kJSClassAttributeNone, NULL);
1010 /* evaluate the script and get return value*/
1011 js_script = JSStringCreateWithUTF8CString(script);
1012 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1013 if (js_result && !JSValueIsUndefined(context, js_result)) {
1014 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1015 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1017 if (js_result_size) {
1018 char js_result_utf8[js_result_size];
1019 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1020 g_string_assign(result, js_result_utf8);
1023 JSStringRelease(js_result_string);
1027 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1029 JSStringRelease(var_name);
1030 JSStringRelease(js_script);
1034 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1036 if (argv_idx(argv, 0))
1037 eval_js(web_view, argv_idx(argv, 0), result);
1041 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1043 if (argv_idx(argv, 0)) {
1044 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1049 while ((line = g_array_index(lines, gchar*, i))) {
1051 js = g_strdup (line);
1053 gchar* newjs = g_strconcat (js, line, NULL);
1060 if (uzbl.state.verbose)
1061 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1063 if (argv_idx (argv, 1)) {
1064 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1068 eval_js (web_view, js, result);
1070 g_array_free (lines, TRUE);
1075 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1076 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1077 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1078 webkit_web_view_unmark_text_matches (page);
1079 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1080 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1084 if (uzbl.state.searchtx) {
1085 if (uzbl.state.verbose)
1086 printf ("Searching: %s\n", uzbl.state.searchtx);
1087 webkit_web_view_set_highlight_text_matches (page, TRUE);
1088 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1093 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1095 search_text(page, argv, TRUE);
1099 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1101 search_text(page, argv, FALSE);
1105 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1106 (void) argv; (void) result;
1107 webkit_web_view_set_highlight_text_matches (page, FALSE);
1112 new_window_load_uri (const gchar * uri) {
1113 if (uzbl.behave.new_window) {
1114 GString *s = g_string_new ("");
1115 g_string_printf(s, "'%s'", uri);
1116 run_handler(uzbl.behave.new_window, s->str);
1119 GString* to_execute = g_string_new ("");
1120 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1122 for (i = 0; entries[i].long_name != NULL; i++) {
1123 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1124 gchar** str = (gchar**)entries[i].arg_data;
1126 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1130 if (uzbl.state.verbose)
1131 printf("\n%s\n", to_execute->str);
1132 g_spawn_command_line_async (to_execute->str, NULL);
1133 g_string_free (to_execute, TRUE);
1137 chain (WebKitWebView *page, GArray *argv, GString *result) {
1138 (void) page; (void) result;
1140 gchar **parts = NULL;
1142 while ((a = argv_idx(argv, i++))) {
1143 parts = g_strsplit (a, " ", 2);
1144 parse_command(parts[0], parts[1], result);
1150 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1154 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1160 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1164 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1170 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1175 int len = strlen(uzbl.state.keycmd);
1176 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1178 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1183 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1190 /* --Statusbar functions-- */
1192 build_progressbar_ascii(int percent) {
1193 int width=uzbl.gui.sbar.progress_w;
1196 GString *bar = g_string_new("");
1198 l = (double)percent*((double)width/100.);
1199 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1201 for(i=0; i<(int)l; i++)
1202 g_string_append(bar, uzbl.gui.sbar.progress_s);
1205 g_string_append(bar, uzbl.gui.sbar.progress_u);
1207 return g_string_free(bar, FALSE);
1209 /* --End Statusbar functions-- */
1212 sharg_append(GArray *a, const gchar *str) {
1213 const gchar *s = (str ? str : "");
1214 g_array_append_val(a, s);
1217 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1219 run_command (const gchar *command, const guint npre, const gchar **args,
1220 const gboolean sync, char **output_stdout) {
1221 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1224 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1225 gchar *pid = itos(getpid());
1226 gchar *xwin = itos(uzbl.xwin);
1228 sharg_append(a, command);
1229 for (i = 0; i < npre; i++) /* add n args before the default vars */
1230 sharg_append(a, args[i]);
1231 sharg_append(a, uzbl.state.config_file);
1232 sharg_append(a, pid);
1233 sharg_append(a, xwin);
1234 sharg_append(a, uzbl.comm.fifo_path);
1235 sharg_append(a, uzbl.comm.socket_path);
1236 sharg_append(a, uzbl.state.uri);
1237 sharg_append(a, uzbl.gui.main_title);
1239 for (i = npre; i < g_strv_length((gchar**)args); i++)
1240 sharg_append(a, args[i]);
1244 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1246 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1247 NULL, NULL, output_stdout, NULL, NULL, &err);
1248 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1249 NULL, NULL, NULL, &err);
1251 if (uzbl.state.verbose) {
1252 GString *s = g_string_new("spawned:");
1253 for (i = 0; i < (a->len); i++) {
1254 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1255 g_string_append_printf(s, " %s", qarg);
1258 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1259 printf("%s\n", s->str);
1260 g_string_free(s, TRUE);
1262 printf("Stdout: %s\n", *output_stdout);
1266 g_printerr("error on run_command: %s\n", err->message);
1271 g_array_free (a, TRUE);
1276 split_quoted(const gchar* src, const gboolean unquote) {
1277 /* split on unquoted space, return array of strings;
1278 remove a layer of quotes and backslashes if unquote */
1279 if (!src) return NULL;
1281 gboolean dq = FALSE;
1282 gboolean sq = FALSE;
1283 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1284 GString *s = g_string_new ("");
1288 for (p = src; *p != '\0'; p++) {
1289 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1290 else if (*p == '\\') { g_string_append_c(s, *p++);
1291 g_string_append_c(s, *p); }
1292 else if ((*p == '"') && unquote && !sq) dq = !dq;
1293 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1295 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1296 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1298 else if ((*p == ' ') && !dq && !sq) {
1299 dup = g_strdup(s->str);
1300 g_array_append_val(a, dup);
1301 g_string_truncate(s, 0);
1302 } else g_string_append_c(s, *p);
1304 dup = g_strdup(s->str);
1305 g_array_append_val(a, dup);
1306 ret = (gchar**)a->data;
1307 g_array_free (a, FALSE);
1308 g_string_free (s, TRUE);
1313 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1314 (void)web_view; (void)result;
1315 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1316 if (argv_idx(argv, 0))
1317 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1321 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1322 (void)web_view; (void)result;
1324 if (argv_idx(argv, 0))
1325 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1326 TRUE, &uzbl.comm.sync_stdout);
1330 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1331 (void)web_view; (void)result;
1332 if (!uzbl.behave.shell_cmd) {
1333 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1338 gchar *spacer = g_strdup("");
1339 g_array_insert_val(argv, 1, spacer);
1340 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1342 for (i = 1; i < g_strv_length(cmd); i++)
1343 g_array_prepend_val(argv, cmd[i]);
1345 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1351 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1352 (void)web_view; (void)result;
1353 if (!uzbl.behave.shell_cmd) {
1354 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1359 gchar *spacer = g_strdup("");
1360 g_array_insert_val(argv, 1, spacer);
1361 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1363 for (i = 1; i < g_strv_length(cmd); i++)
1364 g_array_prepend_val(argv, cmd[i]);
1366 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1367 TRUE, &uzbl.comm.sync_stdout);
1373 parse_command(const char *cmd, const char *param, GString *result) {
1376 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1378 gchar **par = split_quoted(param, TRUE);
1379 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1381 if (c->no_split) { /* don't split */
1382 sharg_append(a, param);
1384 for (i = 0; i < g_strv_length(par); i++)
1385 sharg_append(a, par[i]);
1388 if (result == NULL) {
1389 GString *result_print = g_string_new("");
1391 c->function(uzbl.gui.web_view, a, result_print);
1392 if (result_print->len)
1393 printf("%*s\n", result_print->len, result_print->str);
1395 g_string_free(result_print, TRUE);
1397 c->function(uzbl.gui.web_view, a, result);
1400 g_array_free (a, TRUE);
1403 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1410 if(*uzbl.net.proxy_url == ' '
1411 || uzbl.net.proxy_url == NULL) {
1412 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1413 (GType) SOUP_SESSION_PROXY_URI);
1416 suri = soup_uri_new(uzbl.net.proxy_url);
1417 g_object_set(G_OBJECT(uzbl.net.soup_session),
1418 SOUP_SESSION_PROXY_URI,
1420 soup_uri_free(suri);
1427 if(file_exists(uzbl.gui.icon)) {
1428 if (uzbl.gui.main_window)
1429 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1431 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1437 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1438 g_array_append_val (a, uzbl.state.uri);
1439 load_uri(uzbl.gui.web_view, a, NULL);
1440 g_array_free (a, TRUE);
1444 cmd_always_insert_mode() {
1445 set_insert_mode(uzbl.behave.always_insert_mode);
1451 g_object_set(G_OBJECT(uzbl.net.soup_session),
1452 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1456 cmd_max_conns_host() {
1457 g_object_set(G_OBJECT(uzbl.net.soup_session),
1458 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1463 soup_session_remove_feature
1464 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1465 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1466 /*g_free(uzbl.net.soup_logger);*/
1468 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1469 soup_session_add_feature(uzbl.net.soup_session,
1470 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1473 static WebKitWebSettings*
1475 return webkit_web_view_get_settings(uzbl.gui.web_view);
1480 WebKitWebSettings *ws = view_settings();
1481 if (uzbl.behave.font_size > 0) {
1482 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1485 if (uzbl.behave.monospace_size > 0) {
1486 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1487 uzbl.behave.monospace_size, NULL);
1489 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1490 uzbl.behave.font_size, NULL);
1496 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1500 cmd_disable_plugins() {
1501 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1502 !uzbl.behave.disable_plugins, NULL);
1506 cmd_disable_scripts() {
1507 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1508 !uzbl.behave.disable_scripts, NULL);
1512 cmd_minimum_font_size() {
1513 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1514 uzbl.behave.minimum_font_size, NULL);
1517 cmd_autoload_img() {
1518 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1519 uzbl.behave.autoload_img, NULL);
1524 cmd_autoshrink_img() {
1525 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1526 uzbl.behave.autoshrink_img, NULL);
1531 cmd_enable_spellcheck() {
1532 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1533 uzbl.behave.enable_spellcheck, NULL);
1537 cmd_enable_private() {
1538 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1539 uzbl.behave.enable_private, NULL);
1544 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1545 uzbl.behave.print_bg, NULL);
1550 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1551 uzbl.behave.style_uri, NULL);
1555 cmd_resizable_txt() {
1556 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1557 uzbl.behave.resizable_txt, NULL);
1561 cmd_default_encoding() {
1562 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1563 uzbl.behave.default_encoding, NULL);
1567 cmd_enforce_96dpi() {
1568 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1569 uzbl.behave.enforce_96dpi, NULL);
1573 cmd_caret_browsing() {
1574 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1575 uzbl.behave.caret_browsing, NULL);
1579 cmd_cookie_handler() {
1580 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1581 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1582 if ((g_strcmp0(split[0], "sh") == 0) ||
1583 (g_strcmp0(split[0], "spawn") == 0)) {
1584 g_free (uzbl.behave.cookie_handler);
1585 uzbl.behave.cookie_handler =
1586 g_strdup_printf("sync_%s %s", split[0], split[1]);
1593 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1594 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1595 if ((g_strcmp0(split[0], "sh") == 0) ||
1596 (g_strcmp0(split[0], "spawn") == 0)) {
1597 g_free (uzbl.behave.new_window);
1598 uzbl.behave.new_window =
1599 g_strdup_printf("%s %s", split[0], split[1]);
1606 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1611 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1616 if(uzbl.behave.inject_html) {
1617 webkit_web_view_load_html_string (uzbl.gui.web_view,
1618 uzbl.behave.inject_html, NULL);
1627 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1628 uzbl.behave.modmask = 0;
1630 if(uzbl.behave.modkey)
1631 g_free(uzbl.behave.modkey);
1632 uzbl.behave.modkey = buf;
1634 for (i = 0; modkeys[i].key != NULL; i++) {
1635 if (g_strrstr(buf, modkeys[i].key))
1636 uzbl.behave.modmask |= modkeys[i].mask;
1642 if (*uzbl.net.useragent == ' ') {
1643 g_free (uzbl.net.useragent);
1644 uzbl.net.useragent = NULL;
1646 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1647 uzbl.net.useragent, NULL);
1653 gtk_widget_ref(uzbl.gui.scrolled_win);
1654 gtk_widget_ref(uzbl.gui.mainbar);
1655 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1656 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1658 if(uzbl.behave.status_top) {
1659 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1660 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1663 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1664 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1666 gtk_widget_unref(uzbl.gui.scrolled_win);
1667 gtk_widget_unref(uzbl.gui.mainbar);
1668 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1673 set_var_value(gchar *name, gchar *val) {
1674 uzbl_cmdprop *c = NULL;
1678 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1679 if(!c->writeable) return TRUE;
1681 /* check for the variable type */
1682 if (c->type == TYPE_STR) {
1683 buf = expand(val, 0);
1686 } else if(c->type == TYPE_INT) {
1687 int *ip = (int *)c->ptr;
1688 buf = expand(val, 0);
1689 *ip = (int)strtoul(buf, &endp, 10);
1691 } else if (c->type == TYPE_FLOAT) {
1692 float *fp = (float *)c->ptr;
1693 buf = expand(val, 0);
1694 *fp = strtod(buf, &endp);
1698 /* invoke a command specific function */
1699 if(c->func) c->func();
1706 Behaviour *b = &uzbl.behave;
1708 if(b->html_buffer->str) {
1709 webkit_web_view_load_html_string (uzbl.gui.web_view,
1710 b->html_buffer->str, b->base_url);
1711 g_string_free(b->html_buffer, TRUE);
1712 b->html_buffer = g_string_new("");
1716 enum {M_CMD, M_HTML};
1718 parse_cmd_line(const char *ctl_line, GString *result) {
1719 Behaviour *b = &uzbl.behave;
1722 if(b->mode == M_HTML) {
1723 len = strlen(b->html_endmarker);
1724 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1725 if(len == strlen(ctl_line)-1 &&
1726 !strncmp(b->html_endmarker, ctl_line, len)) {
1728 set_var_value("mode", "0");
1733 set_timeout(b->html_timeout);
1734 g_string_append(b->html_buffer, ctl_line);
1737 else if((ctl_line[0] == '#') /* Comments */
1738 || (ctl_line[0] == ' ')
1739 || (ctl_line[0] == '\n'))
1740 ; /* ignore these lines */
1741 else { /* parse a command */
1743 gchar **tokens = NULL;
1744 len = strlen(ctl_line);
1746 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1747 ctlstrip = g_strndup(ctl_line, len - 1);
1748 else ctlstrip = g_strdup(ctl_line);
1750 tokens = g_strsplit(ctlstrip, " ", 2);
1751 parse_command(tokens[0], tokens[1], result);
1758 build_stream_name(int type, const gchar* dir) {
1759 State *s = &uzbl.state;
1763 str = g_strdup_printf
1764 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1765 } else if (type == SOCKET) {
1766 str = g_strdup_printf
1767 ("%s/uzbl_socket_%s", dir, s->instance_name);
1773 control_fifo(GIOChannel *gio, GIOCondition condition) {
1774 if (uzbl.state.verbose)
1775 printf("triggered\n");
1780 if (condition & G_IO_HUP)
1781 g_error ("Fifo: Read end of pipe died!\n");
1784 g_error ("Fifo: GIOChannel broke\n");
1786 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1787 if (ret == G_IO_STATUS_ERROR) {
1788 g_error ("Fifo: Error reading: %s\n", err->message);
1792 parse_cmd_line(ctl_line, NULL);
1799 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1800 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1801 if (unlink(uzbl.comm.fifo_path) == -1)
1802 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1803 g_free(uzbl.comm.fifo_path);
1804 uzbl.comm.fifo_path = NULL;
1807 if (*dir == ' ') { /* space unsets the variable */
1812 GIOChannel *chan = NULL;
1813 GError *error = NULL;
1814 gchar *path = build_stream_name(FIFO, dir);
1816 if (!file_exists(path)) {
1817 if (mkfifo (path, 0666) == 0) {
1818 // 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.
1819 chan = g_io_channel_new_file(path, "r+", &error);
1821 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1822 if (uzbl.state.verbose)
1823 printf ("init_fifo: created successfully as %s\n", path);
1824 uzbl.comm.fifo_path = path;
1826 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1827 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1828 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1829 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1831 /* if we got this far, there was an error; cleanup */
1832 if (error) g_error_free (error);
1839 control_stdin(GIOChannel *gio, GIOCondition condition) {
1841 gchar *ctl_line = NULL;
1844 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1845 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1848 parse_cmd_line(ctl_line, NULL);
1856 GIOChannel *chan = NULL;
1857 GError *error = NULL;
1859 chan = g_io_channel_unix_new(fileno(stdin));
1861 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1862 g_error ("Stdin: could not add watch\n");
1864 if (uzbl.state.verbose)
1865 printf ("Stdin: watch added successfully\n");
1868 g_error ("Stdin: Error while opening: %s\n", error->message);
1870 if (error) g_error_free (error);
1874 control_socket(GIOChannel *chan) {
1875 struct sockaddr_un remote;
1876 unsigned int t = sizeof(remote);
1878 GIOChannel *clientchan;
1880 clientsock = accept (g_io_channel_unix_get_fd(chan),
1881 (struct sockaddr *) &remote, &t);
1883 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1884 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1885 (GIOFunc) control_client_socket, clientchan);
1892 control_client_socket(GIOChannel *clientchan) {
1894 GString *result = g_string_new("");
1895 GError *error = NULL;
1899 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1900 if (ret == G_IO_STATUS_ERROR) {
1901 g_warning ("Error reading: %s\n", error->message);
1902 g_io_channel_shutdown(clientchan, TRUE, &error);
1904 } else if (ret == G_IO_STATUS_EOF) {
1905 /* shutdown and remove channel watch from main loop */
1906 g_io_channel_shutdown(clientchan, TRUE, &error);
1911 parse_cmd_line (ctl_line, result);
1912 g_string_append_c(result, '\n');
1913 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1915 if (ret == G_IO_STATUS_ERROR) {
1916 g_warning ("Error writing: %s", error->message);
1918 g_io_channel_flush(clientchan, &error);
1921 if (error) g_error_free (error);
1922 g_string_free(result, TRUE);
1928 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1929 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1930 if (unlink(uzbl.comm.socket_path) == -1)
1931 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1932 g_free(uzbl.comm.socket_path);
1933 uzbl.comm.socket_path = NULL;
1941 GIOChannel *chan = NULL;
1943 struct sockaddr_un local;
1944 gchar *path = build_stream_name(SOCKET, dir);
1946 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1948 local.sun_family = AF_UNIX;
1949 strcpy (local.sun_path, path);
1950 unlink (local.sun_path);
1952 len = strlen (local.sun_path) + sizeof (local.sun_family);
1953 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1954 if (uzbl.state.verbose)
1955 printf ("init_socket: opened in %s\n", path);
1958 if( (chan = g_io_channel_unix_new(sock)) ) {
1959 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1960 uzbl.comm.socket_path = path;
1963 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1965 /* if we got this far, there was an error; cleanup */
1972 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1973 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1975 // this function may be called very early when the templates are not set (yet), hence the checks
1977 update_title (void) {
1978 Behaviour *b = &uzbl.behave;
1981 if (b->show_status) {
1982 if (b->title_format_short) {
1983 parsed = expand(b->title_format_short, 0);
1984 if (uzbl.gui.main_window)
1985 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1988 if (b->status_format) {
1989 parsed = expand(b->status_format, 0);
1990 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1993 if (b->status_background) {
1995 gdk_color_parse (b->status_background, &color);
1996 //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)
1997 if (uzbl.gui.main_window)
1998 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1999 else if (uzbl.gui.plug)
2000 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2003 if (b->title_format_long) {
2004 parsed = expand(b->title_format_long, 0);
2005 if (uzbl.gui.main_window)
2006 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2013 key_press_cb (GtkWidget* window, GdkEventKey* event)
2015 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2019 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2020 || 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)
2023 /* turn off insert mode (if always_insert_mode is not used) */
2024 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2025 set_insert_mode(uzbl.behave.always_insert_mode);
2030 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2033 if (event->keyval == GDK_Escape) {
2036 dehilight(uzbl.gui.web_view, NULL, NULL);
2040 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2041 if (event->keyval == GDK_Insert) {
2043 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2044 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2046 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2049 GString* keycmd = g_string_new(uzbl.state.keycmd);
2050 g_string_append (keycmd, str);
2051 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2058 if (event->keyval == GDK_BackSpace)
2059 keycmd_bs(NULL, NULL, NULL);
2061 gboolean key_ret = FALSE;
2062 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2065 GString* keycmd = g_string_new(uzbl.state.keycmd);
2066 g_string_append(keycmd, event->string);
2067 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2070 run_keycmd(key_ret);
2072 if (key_ret) return (!uzbl.behave.insert_mode);
2077 run_keycmd(const gboolean key_ret) {
2078 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2080 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2082 parse_command(act->name, act->param, NULL);
2086 /* try if it's an incremental keycmd or one that takes args, and run it */
2087 GString* short_keys = g_string_new ("");
2088 GString* short_keys_inc = g_string_new ("");
2090 guint len = strlen(uzbl.state.keycmd);
2091 for (i=0; i<len; i++) {
2092 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2093 g_string_assign(short_keys_inc, short_keys->str);
2094 g_string_append_c(short_keys, '_');
2095 g_string_append_c(short_keys_inc, '*');
2097 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2098 /* run normal cmds only if return was pressed */
2099 exec_paramcmd(act, i);
2102 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2103 if (key_ret) /* just quit the incremental command on return */
2105 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2109 g_string_truncate(short_keys, short_keys->len - 1);
2111 g_string_free (short_keys, TRUE);
2112 g_string_free (short_keys_inc, TRUE);
2116 exec_paramcmd(const Action *act, const guint i) {
2117 GString *parampart = g_string_new (uzbl.state.keycmd);
2118 GString *actionname = g_string_new ("");
2119 GString *actionparam = g_string_new ("");
2120 g_string_erase (parampart, 0, i+1);
2122 g_string_printf (actionname, act->name, parampart->str);
2124 g_string_printf (actionparam, act->param, parampart->str);
2125 parse_command(actionname->str, actionparam->str, NULL);
2126 g_string_free(actionname, TRUE);
2127 g_string_free(actionparam, TRUE);
2128 g_string_free(parampart, TRUE);
2136 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2137 //main_window_ref = g_object_ref(scrolled_window);
2138 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
2140 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2141 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2143 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2144 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2145 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2146 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2147 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2148 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2149 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2150 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2151 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2152 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2153 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2155 return scrolled_window;
2162 g->mainbar = gtk_hbox_new (FALSE, 0);
2164 /* keep a reference to the bar so we can re-pack it at runtime*/
2165 //sbar_ref = g_object_ref(g->mainbar);
2167 g->mainbar_label = gtk_label_new ("");
2168 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2169 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2170 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2171 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2172 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2173 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2178 GtkWidget* create_window () {
2179 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2180 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2181 gtk_widget_set_name (window, "Uzbl browser");
2182 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2183 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2189 GtkPlug* create_plug () {
2190 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2191 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2192 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2199 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2201 If actname is one that calls an external command, this function will inject
2202 newargs in front of the user-provided args in that command line. They will
2203 come become after the body of the script (in sh) or after the name of
2204 the command to execute (in spawn).
2205 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2206 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2208 The return value consist of two strings: the action (sh, ...) and its args.
2210 If act is not one that calls an external command, then the given action merely
2213 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2214 gchar *actdup = g_strdup(actname);
2215 g_array_append_val(rets, actdup);
2217 if ((g_strcmp0(actname, "spawn") == 0) ||
2218 (g_strcmp0(actname, "sh") == 0) ||
2219 (g_strcmp0(actname, "sync_spawn") == 0) ||
2220 (g_strcmp0(actname, "sync_sh") == 0)) {
2222 GString *a = g_string_new("");
2223 gchar **spawnparts = split_quoted(origargs, FALSE);
2224 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2225 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2227 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2228 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2230 g_array_append_val(rets, a->str);
2231 g_string_free(a, FALSE);
2232 g_strfreev(spawnparts);
2234 gchar *origdup = g_strdup(origargs);
2235 g_array_append_val(rets, origdup);
2237 return (gchar**)g_array_free(rets, FALSE);
2241 run_handler (const gchar *act, const gchar *args) {
2242 /* Consider this code a temporary hack to make the handlers usable.
2243 In practice, all this splicing, injection, and reconstruction is
2244 inefficient, annoying and hard to manage. Potential pitfalls arise
2245 when the handler specific args 1) are not quoted (the handler
2246 callbacks should take care of this) 2) are quoted but interfere
2247 with the users' own quotation. A more ideal solution is
2248 to refactor parse_command so that it doesn't just take a string
2249 and execute it; rather than that, we should have a function which
2250 returns the argument vector parsed from the string. This vector
2251 could be modified (e.g. insert additional args into it) before
2252 passing it to the next function that actually executes it. Though
2253 it still isn't perfect for chain actions.. will reconsider & re-
2254 factor when I have the time. -duc */
2256 char **parts = g_strsplit(act, " ", 2);
2258 if (g_strcmp0(parts[0], "chain") == 0) {
2259 GString *newargs = g_string_new("");
2260 gchar **chainparts = split_quoted(parts[1], FALSE);
2262 /* for every argument in the chain, inject the handler args
2263 and make sure the new parts are wrapped in quotes */
2264 gchar **cp = chainparts;
2266 gchar *quotless = NULL;
2267 gchar **spliced_quotless = NULL; // sigh -_-;
2268 gchar **inpart = NULL;
2271 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2273 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2274 } else quotless = g_strdup(*cp);
2276 spliced_quotless = g_strsplit(quotless, " ", 2);
2277 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2278 g_strfreev(spliced_quotless);
2280 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2286 parse_command(parts[0], &(newargs->str[1]), NULL);
2287 g_string_free(newargs, TRUE);
2288 g_strfreev(chainparts);
2291 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2292 parse_command(inparts[0], inparts[1], NULL);
2300 add_binding (const gchar *key, const gchar *act) {
2301 char **parts = g_strsplit(act, " ", 2);
2308 if (uzbl.state.verbose)
2309 printf ("Binding %-10s : %s\n", key, act);
2310 action = new_action(parts[0], parts[1]);
2312 if (g_hash_table_remove (uzbl.bindings, key))
2313 g_warning ("Overwriting existing binding for \"%s\"", key);
2314 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2319 get_xdg_var (XDG_Var xdg) {
2320 const gchar* actual_value = getenv (xdg.environmental);
2321 const gchar* home = getenv ("HOME");
2322 gchar* return_value;
2324 if (! actual_value || strcmp (actual_value, "") == 0) {
2325 if (xdg.default_value) {
2326 return_value = str_replace ("~", home, xdg.default_value);
2328 return_value = NULL;
2331 return_value = str_replace("~", home, actual_value);
2334 return return_value;
2338 find_xdg_file (int xdg_type, char* filename) {
2339 /* xdg_type = 0 => config
2340 xdg_type = 1 => data
2341 xdg_type = 2 => cache*/
2343 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2344 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2347 gchar* temporary_string;
2351 if (! file_exists (temporary_file) && xdg_type != 2) {
2352 buf = get_xdg_var (XDG[3 + xdg_type]);
2353 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2356 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2357 g_free (temporary_file);
2358 temporary_file = g_strconcat (temporary_string, filename, NULL);
2362 //g_free (temporary_string); - segfaults.
2364 if (file_exists (temporary_file)) {
2365 return temporary_file;
2372 State *s = &uzbl.state;
2373 Network *n = &uzbl.net;
2375 for (i = 0; default_config[i].command != NULL; i++) {
2376 parse_cmd_line(default_config[i].command, NULL);
2379 if (g_strcmp0(s->config_file, "-") == 0) {
2380 s->config_file = NULL;
2384 else if (!s->config_file) {
2385 s->config_file = find_xdg_file (0, "/uzbl/config");
2388 if (s->config_file) {
2389 GArray* lines = read_file_by_line (s->config_file);
2393 while ((line = g_array_index(lines, gchar*, i))) {
2394 parse_cmd_line (line, NULL);
2398 g_array_free (lines, TRUE);
2400 if (uzbl.state.verbose)
2401 printf ("No configuration file loaded.\n");
2404 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2407 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2410 if (!uzbl.behave.cookie_handler)
2413 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2414 GString *s = g_string_new ("");
2415 SoupURI * soup_uri = soup_message_get_uri(msg);
2416 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2417 run_handler(uzbl.behave.cookie_handler, s->str);
2419 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2420 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2421 if ( p != NULL ) *p = '\0';
2422 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2424 if (uzbl.comm.sync_stdout)
2425 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2427 g_string_free(s, TRUE);
2431 save_cookies (SoupMessage *msg, gpointer user_data){
2435 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2436 cookie = soup_cookie_to_set_cookie_header(ck->data);
2437 SoupURI * soup_uri = soup_message_get_uri(msg);
2438 GString *s = g_string_new ("");
2439 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2440 run_handler(uzbl.behave.cookie_handler, s->str);
2442 g_string_free(s, TRUE);
2447 /* --- WEBINSPECTOR --- */
2449 hide_window_cb(GtkWidget *widget, gpointer data) {
2452 gtk_widget_hide(widget);
2455 static WebKitWebView*
2456 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2459 (void) web_inspector;
2460 GtkWidget* scrolled_window;
2461 GtkWidget* new_web_view;
2464 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2465 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2466 G_CALLBACK(hide_window_cb), NULL);
2468 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2469 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2470 gtk_widget_show(g->inspector_window);
2472 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2473 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2474 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2475 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2476 gtk_widget_show(scrolled_window);
2478 new_web_view = webkit_web_view_new();
2479 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2481 return WEBKIT_WEB_VIEW(new_web_view);
2485 inspector_show_window_cb (WebKitWebInspector* inspector){
2487 gtk_widget_show(uzbl.gui.inspector_window);
2491 /* TODO: Add variables and code to make use of these functions */
2493 inspector_close_window_cb (WebKitWebInspector* inspector){
2499 inspector_attach_window_cb (WebKitWebInspector* inspector){
2505 inspector_detach_window_cb (WebKitWebInspector* inspector){
2511 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2517 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2523 set_up_inspector() {
2525 WebKitWebSettings *settings = view_settings();
2526 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2528 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2529 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2530 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2531 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2532 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2533 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2534 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2536 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2540 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2542 uzbl_cmdprop *c = v;
2547 if(c->type == TYPE_STR)
2548 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2549 else if(c->type == TYPE_INT)
2550 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2554 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2558 printf("bind %s = %s %s\n", (char *)k ,
2559 (char *)a->name, a->param?(char *)a->param:"");
2564 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2565 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2568 /* set up gtk, gobject, variable defaults and other things that tests and other
2569 * external applications need to do anyhow */
2571 initialize(int argc, char *argv[]) {
2572 gtk_init (&argc, &argv);
2573 if (!g_thread_supported ())
2574 g_thread_init (NULL);
2575 uzbl.state.executable_path = g_strdup(argv[0]);
2576 uzbl.state.selected_url = NULL;
2577 uzbl.state.searchtx = NULL;
2579 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2580 g_option_context_add_main_entries (context, entries, NULL);
2581 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2582 g_option_context_parse (context, &argc, &argv, NULL);
2583 g_option_context_free(context);
2585 if (uzbl.behave.print_version) {
2586 printf("Commit: %s\n", COMMIT);
2590 /* initialize hash table */
2591 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2593 uzbl.net.soup_session = webkit_get_default_session();
2594 uzbl.state.keycmd = g_strdup("");
2596 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2597 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2598 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2599 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2600 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2601 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2603 uzbl.gui.sbar.progress_s = g_strdup("=");
2604 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2605 uzbl.gui.sbar.progress_w = 10;
2607 /* HTML mode defaults*/
2608 uzbl.behave.html_buffer = g_string_new("");
2609 uzbl.behave.html_endmarker = g_strdup(".");
2610 uzbl.behave.html_timeout = 60;
2611 uzbl.behave.base_url = g_strdup("http://invalid");
2613 /* default mode indicators */
2614 uzbl.behave.insert_indicator = g_strdup("I");
2615 uzbl.behave.cmd_indicator = g_strdup("C");
2616 set_insert_mode(FALSE);
2618 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2619 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2620 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2621 uzbl.info.arch = ARCH;
2622 uzbl.info.commit = COMMIT;
2625 make_var_to_name_hash();
2627 uzbl.gui.scrolled_win = create_browser();
2630 #ifndef UZBL_LIBRARY
2633 main (int argc, char* argv[]) {
2634 initialize(argc, argv);
2636 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2640 /* initial packing */
2641 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2642 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2644 if (uzbl.state.socket_id) {
2645 uzbl.gui.plug = create_plug ();
2646 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2647 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2649 uzbl.gui.main_window = create_window ();
2650 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2651 gtk_widget_show_all (uzbl.gui.main_window);
2652 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2655 if(!uzbl.state.instance_name)
2656 uzbl.state.instance_name = itos((int)uzbl.xwin);
2658 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2660 if (uzbl.state.verbose) {
2661 printf("Uzbl start location: %s\n", argv[0]);
2662 if (uzbl.state.socket_id)
2663 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2665 printf("window_id %i\n",(int) uzbl.xwin);
2666 printf("pid %i\n", getpid ());
2667 printf("name: %s\n", uzbl.state.instance_name);
2670 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2671 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2672 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2673 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2674 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2676 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2677 gboolean verbose_override = uzbl.state.verbose;
2681 if (!uzbl.behave.show_status)
2682 gtk_widget_hide(uzbl.gui.mainbar);
2689 if (verbose_override > uzbl.state.verbose)
2690 uzbl.state.verbose = verbose_override;
2693 set_var_value("uri", uri_override);
2694 g_free(uri_override);
2695 } else if (uzbl.state.uri)
2696 cmd_load_uri(uzbl.gui.web_view, NULL);
2701 return EXIT_SUCCESS;
2705 /* vi: set et ts=4: */