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, NULL)}, /* XXX */
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 /* XXX set_var_value instead? */
889 void set_insert_mode(gboolean mode) {
890 uzbl.behave.insert_mode = mode;
891 uzbl.gui.sbar.mode_indicator = (mode ?
892 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
896 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
897 (void) page; (void) result;
899 if (argv_idx(argv, 0)) {
900 if (strcmp (argv_idx(argv, 0), "0") == 0) {
901 set_insert_mode(FALSE);
903 set_insert_mode(TRUE);
906 set_insert_mode( !uzbl.behave.insert_mode );
913 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
916 if (argv_idx(argv, 0)) {
917 GString* newuri = g_string_new (argv_idx(argv, 0));
918 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
919 run_js(web_view, argv, NULL);
922 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
923 g_string_prepend (newuri, "http://");
924 /* if we do handle cookies, ask our handler for them */
925 webkit_web_view_load_uri (web_view, newuri->str);
926 g_string_free (newuri, TRUE);
934 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
935 size_t argumentCount, const JSValueRef arguments[],
936 JSValueRef* exception) {
941 JSStringRef js_result_string;
942 GString *result = g_string_new("");
944 if (argumentCount >= 1) {
945 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
946 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
947 char ctl_line[arg_size];
948 JSStringGetUTF8CString(arg, ctl_line, arg_size);
950 parse_cmd_line(ctl_line, result);
952 JSStringRelease(arg);
954 js_result_string = JSStringCreateWithUTF8CString(result->str);
956 g_string_free(result, TRUE);
958 return JSValueMakeString(ctx, js_result_string);
961 static JSStaticFunction js_static_functions[] = {
962 {"run", js_run_command, kJSPropertyAttributeNone},
967 /* This function creates the class and its definition, only once */
968 if (!uzbl.js.initialized) {
969 /* it would be pretty cool to make this dynamic */
970 uzbl.js.classdef = kJSClassDefinitionEmpty;
971 uzbl.js.classdef.staticFunctions = js_static_functions;
973 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
979 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
980 WebKitWebFrame *frame;
981 JSGlobalContextRef context;
982 JSObjectRef globalobject;
983 JSStringRef var_name;
985 JSStringRef js_script;
986 JSValueRef js_result;
987 JSStringRef js_result_string;
988 size_t js_result_size;
992 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
993 context = webkit_web_frame_get_global_context(frame);
994 globalobject = JSContextGetGlobalObject(context);
996 /* uzbl javascript namespace */
997 var_name = JSStringCreateWithUTF8CString("Uzbl");
998 JSObjectSetProperty(context, globalobject, var_name,
999 JSObjectMake(context, uzbl.js.classref, NULL),
1000 kJSClassAttributeNone, NULL);
1002 /* evaluate the script and get return value*/
1003 js_script = JSStringCreateWithUTF8CString(script);
1004 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1005 if (js_result && !JSValueIsUndefined(context, js_result)) {
1006 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1007 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1009 if (js_result_size) {
1010 char js_result_utf8[js_result_size];
1011 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1012 g_string_assign(result, js_result_utf8);
1015 JSStringRelease(js_result_string);
1019 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1021 JSStringRelease(var_name);
1022 JSStringRelease(js_script);
1026 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1028 if (argv_idx(argv, 0))
1029 eval_js(web_view, argv_idx(argv, 0), result);
1033 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1035 if (argv_idx(argv, 0)) {
1036 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1041 while ((line = g_array_index(lines, gchar*, i))) {
1043 js = g_strdup (line);
1045 gchar* newjs = g_strconcat (js, line, NULL);
1052 if (uzbl.state.verbose)
1053 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1055 if (argv_idx (argv, 1)) {
1056 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1060 eval_js (web_view, js, result);
1062 g_array_free (lines, TRUE);
1067 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1068 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1069 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1070 webkit_web_view_unmark_text_matches (page);
1071 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1072 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1076 if (uzbl.state.searchtx) {
1077 if (uzbl.state.verbose)
1078 printf ("Searching: %s\n", uzbl.state.searchtx);
1079 webkit_web_view_set_highlight_text_matches (page, TRUE);
1080 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1085 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1087 search_text(page, argv, TRUE);
1091 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1093 search_text(page, argv, FALSE);
1097 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1098 (void) argv; (void) result;
1099 webkit_web_view_set_highlight_text_matches (page, FALSE);
1104 new_window_load_uri (const gchar * uri) {
1105 if (uzbl.behave.new_window) {
1106 GString *s = g_string_new ("");
1107 g_string_printf(s, "'%s'", uri);
1108 run_handler(uzbl.behave.new_window, s->str);
1111 GString* to_execute = g_string_new ("");
1112 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1114 for (i = 0; entries[i].long_name != NULL; i++) {
1115 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1116 gchar** str = (gchar**)entries[i].arg_data;
1118 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1122 if (uzbl.state.verbose)
1123 printf("\n%s\n", to_execute->str);
1124 g_spawn_command_line_async (to_execute->str, NULL);
1125 g_string_free (to_execute, TRUE);
1129 chain (WebKitWebView *page, GArray *argv, GString *result) {
1130 (void) page; (void) result;
1132 gchar **parts = NULL;
1134 while ((a = argv_idx(argv, i++))) {
1135 parts = g_strsplit (a, " ", 2);
1136 parse_command(parts[0], parts[1], result);
1142 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1146 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1152 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1156 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1162 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1167 int len = strlen(uzbl.state.keycmd);
1168 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1170 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1175 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1182 /* --Statusbar functions-- */
1184 build_progressbar_ascii(int percent) {
1185 int width=uzbl.gui.sbar.progress_w;
1188 GString *bar = g_string_new("");
1190 l = (double)percent*((double)width/100.);
1191 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1193 for(i=0; i<(int)l; i++)
1194 g_string_append(bar, uzbl.gui.sbar.progress_s);
1197 g_string_append(bar, uzbl.gui.sbar.progress_u);
1199 return g_string_free(bar, FALSE);
1201 /* --End Statusbar functions-- */
1204 sharg_append(GArray *a, const gchar *str) {
1205 const gchar *s = (str ? str : "");
1206 g_array_append_val(a, s);
1209 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1211 run_command (const gchar *command, const guint npre, const gchar **args,
1212 const gboolean sync, char **output_stdout) {
1213 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1216 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1217 gchar *pid = itos(getpid());
1218 gchar *xwin = itos(uzbl.xwin);
1220 sharg_append(a, command);
1221 for (i = 0; i < npre; i++) /* add n args before the default vars */
1222 sharg_append(a, args[i]);
1223 sharg_append(a, uzbl.state.config_file);
1224 sharg_append(a, pid);
1225 sharg_append(a, xwin);
1226 sharg_append(a, uzbl.comm.fifo_path);
1227 sharg_append(a, uzbl.comm.socket_path);
1228 sharg_append(a, uzbl.state.uri);
1229 sharg_append(a, uzbl.gui.main_title);
1231 for (i = npre; i < g_strv_length((gchar**)args); i++)
1232 sharg_append(a, args[i]);
1236 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1238 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1239 NULL, NULL, output_stdout, NULL, NULL, &err);
1240 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1241 NULL, NULL, NULL, &err);
1243 if (uzbl.state.verbose) {
1244 GString *s = g_string_new("spawned:");
1245 for (i = 0; i < (a->len); i++) {
1246 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1247 g_string_append_printf(s, " %s", qarg);
1250 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1251 printf("%s\n", s->str);
1252 g_string_free(s, TRUE);
1254 printf("Stdout: %s\n", *output_stdout);
1258 g_printerr("error on run_command: %s\n", err->message);
1263 g_array_free (a, TRUE);
1268 split_quoted(const gchar* src, const gboolean unquote) {
1269 /* split on unquoted space, return array of strings;
1270 remove a layer of quotes and backslashes if unquote */
1271 if (!src) return NULL;
1273 gboolean dq = FALSE;
1274 gboolean sq = FALSE;
1275 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1276 GString *s = g_string_new ("");
1280 for (p = src; *p != '\0'; p++) {
1281 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1282 else if (*p == '\\') { g_string_append_c(s, *p++);
1283 g_string_append_c(s, *p); }
1284 else if ((*p == '"') && unquote && !sq) dq = !dq;
1285 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1287 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1288 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1290 else if ((*p == ' ') && !dq && !sq) {
1291 dup = g_strdup(s->str);
1292 g_array_append_val(a, dup);
1293 g_string_truncate(s, 0);
1294 } else g_string_append_c(s, *p);
1296 dup = g_strdup(s->str);
1297 g_array_append_val(a, dup);
1298 ret = (gchar**)a->data;
1299 g_array_free (a, FALSE);
1300 g_string_free (s, TRUE);
1305 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1306 (void)web_view; (void)result;
1307 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1308 if (argv_idx(argv, 0))
1309 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1313 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1314 (void)web_view; (void)result;
1316 if (argv_idx(argv, 0))
1317 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1318 TRUE, &uzbl.comm.sync_stdout);
1322 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1323 (void)web_view; (void)result;
1324 if (!uzbl.behave.shell_cmd) {
1325 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1330 gchar *spacer = g_strdup("");
1331 g_array_insert_val(argv, 1, spacer);
1332 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1334 for (i = 1; i < g_strv_length(cmd); i++)
1335 g_array_prepend_val(argv, cmd[i]);
1337 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1343 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1344 (void)web_view; (void)result;
1345 if (!uzbl.behave.shell_cmd) {
1346 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1351 gchar *spacer = g_strdup("");
1352 g_array_insert_val(argv, 1, spacer);
1353 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1355 for (i = 1; i < g_strv_length(cmd); i++)
1356 g_array_prepend_val(argv, cmd[i]);
1358 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1359 TRUE, &uzbl.comm.sync_stdout);
1365 parse_command(const char *cmd, const char *param, GString *result) {
1368 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1370 gchar **par = split_quoted(param, TRUE);
1371 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1373 if (c->no_split) { /* don't split */
1374 sharg_append(a, param);
1376 for (i = 0; i < g_strv_length(par); i++)
1377 sharg_append(a, par[i]);
1380 if (result == NULL) {
1381 GString *result_print = g_string_new("");
1383 c->function(uzbl.gui.web_view, a, result_print);
1384 if (result_print->len)
1385 printf("%*s\n", result_print->len, result_print->str);
1387 g_string_free(result_print, TRUE);
1389 c->function(uzbl.gui.web_view, a, result);
1392 g_array_free (a, TRUE);
1395 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1402 if(*uzbl.net.proxy_url == ' '
1403 || uzbl.net.proxy_url == NULL) {
1404 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1405 (GType) SOUP_SESSION_PROXY_URI);
1408 suri = soup_uri_new(uzbl.net.proxy_url);
1409 g_object_set(G_OBJECT(uzbl.net.soup_session),
1410 SOUP_SESSION_PROXY_URI,
1412 soup_uri_free(suri);
1419 if(file_exists(uzbl.gui.icon)) {
1420 if (uzbl.gui.main_window)
1421 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1423 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1429 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1430 g_array_append_val (a, uzbl.state.uri);
1431 load_uri(uzbl.gui.web_view, a, NULL);
1432 g_array_free (a, TRUE);
1436 cmd_always_insert_mode() {
1437 set_insert_mode(uzbl.behave.always_insert_mode);
1443 g_object_set(G_OBJECT(uzbl.net.soup_session),
1444 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1448 cmd_max_conns_host() {
1449 g_object_set(G_OBJECT(uzbl.net.soup_session),
1450 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1455 soup_session_remove_feature
1456 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1457 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1458 /*g_free(uzbl.net.soup_logger);*/
1460 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1461 soup_session_add_feature(uzbl.net.soup_session,
1462 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1465 static WebKitWebSettings*
1467 return webkit_web_view_get_settings(uzbl.gui.web_view);
1472 WebKitWebSettings *ws = view_settings();
1473 if (uzbl.behave.font_size > 0) {
1474 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1477 if (uzbl.behave.monospace_size > 0) {
1478 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1479 uzbl.behave.monospace_size, NULL);
1481 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1482 uzbl.behave.font_size, NULL);
1488 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1492 cmd_disable_plugins() {
1493 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1494 !uzbl.behave.disable_plugins, NULL);
1498 cmd_disable_scripts() {
1499 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1500 !uzbl.behave.disable_scripts, NULL);
1504 cmd_minimum_font_size() {
1505 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1506 uzbl.behave.minimum_font_size, NULL);
1509 cmd_autoload_img() {
1510 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1511 uzbl.behave.autoload_img, NULL);
1516 cmd_autoshrink_img() {
1517 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1518 uzbl.behave.autoshrink_img, NULL);
1523 cmd_enable_spellcheck() {
1524 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1525 uzbl.behave.enable_spellcheck, NULL);
1529 cmd_enable_private() {
1530 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1531 uzbl.behave.enable_private, NULL);
1536 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1537 uzbl.behave.print_bg, NULL);
1542 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1543 uzbl.behave.style_uri, NULL);
1547 cmd_resizable_txt() {
1548 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1549 uzbl.behave.resizable_txt, NULL);
1553 cmd_default_encoding() {
1554 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1555 uzbl.behave.default_encoding, NULL);
1559 cmd_enforce_96dpi() {
1560 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1561 uzbl.behave.enforce_96dpi, NULL);
1565 cmd_caret_browsing() {
1566 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1567 uzbl.behave.caret_browsing, NULL);
1571 cmd_cookie_handler() {
1572 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1573 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1574 if ((g_strcmp0(split[0], "sh") == 0) ||
1575 (g_strcmp0(split[0], "spawn") == 0)) {
1576 g_free (uzbl.behave.cookie_handler);
1577 uzbl.behave.cookie_handler =
1578 g_strdup_printf("sync_%s %s", split[0], split[1]);
1585 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1586 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1587 if ((g_strcmp0(split[0], "sh") == 0) ||
1588 (g_strcmp0(split[0], "spawn") == 0)) {
1589 g_free (uzbl.behave.new_window);
1590 uzbl.behave.new_window =
1591 g_strdup_printf("%s %s", split[0], split[1]);
1598 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1603 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1608 if(uzbl.behave.inject_html) {
1609 webkit_web_view_load_html_string (uzbl.gui.web_view,
1610 uzbl.behave.inject_html, NULL);
1619 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1620 uzbl.behave.modmask = 0;
1622 if(uzbl.behave.modkey)
1623 g_free(uzbl.behave.modkey);
1624 uzbl.behave.modkey = buf;
1626 for (i = 0; modkeys[i].key != NULL; i++) {
1627 if (g_strrstr(buf, modkeys[i].key))
1628 uzbl.behave.modmask |= modkeys[i].mask;
1634 if (*uzbl.net.useragent == ' ') {
1635 g_free (uzbl.net.useragent);
1636 uzbl.net.useragent = NULL;
1638 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1639 uzbl.net.useragent, NULL);
1645 gtk_widget_ref(uzbl.gui.scrolled_win);
1646 gtk_widget_ref(uzbl.gui.mainbar);
1647 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1648 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1650 if(uzbl.behave.status_top) {
1651 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1652 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1655 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1656 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1658 gtk_widget_unref(uzbl.gui.scrolled_win);
1659 gtk_widget_unref(uzbl.gui.mainbar);
1660 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1665 set_var_value(gchar *name, gchar *val) {
1666 uzbl_cmdprop *c = NULL;
1670 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1671 if(!c->writeable) return TRUE;
1673 /* check for the variable type */
1674 if (c->type == TYPE_STR) {
1675 buf = expand(val, 0);
1678 } else if(c->type == TYPE_INT) {
1679 int *ip = (int *)c->ptr;
1680 buf = expand(val, 0);
1681 *ip = (int)strtoul(buf, &endp, 10);
1683 } else if (c->type == TYPE_FLOAT) {
1684 float *fp = (float *)c->ptr;
1685 buf = expand(val, 0);
1686 *fp = strtod(buf, &endp);
1690 /* invoke a command specific function */
1691 if(c->func) c->func();
1698 Behaviour *b = &uzbl.behave;
1700 if(b->html_buffer->str) {
1701 webkit_web_view_load_html_string (uzbl.gui.web_view,
1702 b->html_buffer->str, b->base_url);
1703 g_string_free(b->html_buffer, TRUE);
1704 b->html_buffer = g_string_new("");
1708 enum {M_CMD, M_HTML};
1710 parse_cmd_line(const char *ctl_line, GString *result) {
1711 Behaviour *b = &uzbl.behave;
1714 if(b->mode == M_HTML) {
1715 len = strlen(b->html_endmarker);
1716 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1717 if(len == strlen(ctl_line)-1 &&
1718 !strncmp(b->html_endmarker, ctl_line, len)) {
1720 set_var_value("mode", "0");
1725 set_timeout(b->html_timeout);
1726 g_string_append(b->html_buffer, ctl_line);
1729 else if((ctl_line[0] == '#') /* Comments */
1730 || (ctl_line[0] == ' ')
1731 || (ctl_line[0] == '\n'))
1732 ; /* ignore these lines */
1733 else { /* parse a command */
1735 gchar **tokens = NULL;
1736 len = strlen(ctl_line);
1738 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1739 ctlstrip = g_strndup(ctl_line, len - 1);
1740 else ctlstrip = g_strdup(ctl_line);
1742 tokens = g_strsplit(ctlstrip, " ", 2);
1743 parse_command(tokens[0], tokens[1], result);
1750 build_stream_name(int type, const gchar* dir) {
1751 State *s = &uzbl.state;
1755 str = g_strdup_printf
1756 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1757 } else if (type == SOCKET) {
1758 str = g_strdup_printf
1759 ("%s/uzbl_socket_%s", dir, s->instance_name);
1765 control_fifo(GIOChannel *gio, GIOCondition condition) {
1766 if (uzbl.state.verbose)
1767 printf("triggered\n");
1772 if (condition & G_IO_HUP)
1773 g_error ("Fifo: Read end of pipe died!\n");
1776 g_error ("Fifo: GIOChannel broke\n");
1778 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1779 if (ret == G_IO_STATUS_ERROR) {
1780 g_error ("Fifo: Error reading: %s\n", err->message);
1784 parse_cmd_line(ctl_line, NULL);
1791 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1792 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1793 if (unlink(uzbl.comm.fifo_path) == -1)
1794 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1795 g_free(uzbl.comm.fifo_path);
1796 uzbl.comm.fifo_path = NULL;
1799 if (*dir == ' ') { /* space unsets the variable */
1804 GIOChannel *chan = NULL;
1805 GError *error = NULL;
1806 gchar *path = build_stream_name(FIFO, dir);
1808 if (!file_exists(path)) {
1809 if (mkfifo (path, 0666) == 0) {
1810 // 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.
1811 chan = g_io_channel_new_file(path, "r+", &error);
1813 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1814 if (uzbl.state.verbose)
1815 printf ("init_fifo: created successfully as %s\n", path);
1816 uzbl.comm.fifo_path = path;
1818 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1819 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1820 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1821 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1823 /* if we got this far, there was an error; cleanup */
1824 if (error) g_error_free (error);
1831 control_stdin(GIOChannel *gio, GIOCondition condition) {
1833 gchar *ctl_line = NULL;
1836 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1837 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1840 parse_cmd_line(ctl_line, NULL);
1848 GIOChannel *chan = NULL;
1849 GError *error = NULL;
1851 chan = g_io_channel_unix_new(fileno(stdin));
1853 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1854 g_error ("Stdin: could not add watch\n");
1856 if (uzbl.state.verbose)
1857 printf ("Stdin: watch added successfully\n");
1860 g_error ("Stdin: Error while opening: %s\n", error->message);
1862 if (error) g_error_free (error);
1866 control_socket(GIOChannel *chan) {
1867 struct sockaddr_un remote;
1868 unsigned int t = sizeof(remote);
1870 GIOChannel *clientchan;
1872 clientsock = accept (g_io_channel_unix_get_fd(chan),
1873 (struct sockaddr *) &remote, &t);
1875 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1876 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1877 (GIOFunc) control_client_socket, clientchan);
1884 control_client_socket(GIOChannel *clientchan) {
1886 GString *result = g_string_new("");
1887 GError *error = NULL;
1891 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1892 if (ret == G_IO_STATUS_ERROR) {
1893 g_warning ("Error reading: %s\n", error->message);
1894 g_io_channel_shutdown(clientchan, TRUE, &error);
1896 } else if (ret == G_IO_STATUS_EOF) {
1897 /* shutdown and remove channel watch from main loop */
1898 g_io_channel_shutdown(clientchan, TRUE, &error);
1903 parse_cmd_line (ctl_line, result);
1904 g_string_append_c(result, '\n');
1905 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1907 if (ret == G_IO_STATUS_ERROR) {
1908 g_warning ("Error writing: %s", error->message);
1910 g_io_channel_flush(clientchan, &error);
1913 if (error) g_error_free (error);
1914 g_string_free(result, TRUE);
1920 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1921 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1922 if (unlink(uzbl.comm.socket_path) == -1)
1923 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1924 g_free(uzbl.comm.socket_path);
1925 uzbl.comm.socket_path = NULL;
1933 GIOChannel *chan = NULL;
1935 struct sockaddr_un local;
1936 gchar *path = build_stream_name(SOCKET, dir);
1938 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1940 local.sun_family = AF_UNIX;
1941 strcpy (local.sun_path, path);
1942 unlink (local.sun_path);
1944 len = strlen (local.sun_path) + sizeof (local.sun_family);
1945 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1946 if (uzbl.state.verbose)
1947 printf ("init_socket: opened in %s\n", path);
1950 if( (chan = g_io_channel_unix_new(sock)) ) {
1951 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1952 uzbl.comm.socket_path = path;
1955 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1957 /* if we got this far, there was an error; cleanup */
1964 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1965 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1967 // this function may be called very early when the templates are not set (yet), hence the checks
1969 update_title (void) {
1970 Behaviour *b = &uzbl.behave;
1973 if (b->show_status) {
1974 if (b->title_format_short) {
1975 parsed = expand(b->title_format_short, 0);
1976 if (uzbl.gui.main_window)
1977 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1980 if (b->status_format) {
1981 parsed = expand(b->status_format, 0);
1982 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1985 if (b->status_background) {
1987 gdk_color_parse (b->status_background, &color);
1988 //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)
1989 if (uzbl.gui.main_window)
1990 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1991 else if (uzbl.gui.plug)
1992 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
1995 if (b->title_format_long) {
1996 parsed = expand(b->title_format_long, 0);
1997 if (uzbl.gui.main_window)
1998 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2005 key_press_cb (GtkWidget* window, GdkEventKey* event)
2007 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2011 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2012 || 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)
2015 /* turn off insert mode (if always_insert_mode is not used) */
2016 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2017 set_insert_mode(uzbl.behave.always_insert_mode);
2022 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2025 if (event->keyval == GDK_Escape) {
2028 dehilight(uzbl.gui.web_view, NULL, NULL);
2032 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2033 if (event->keyval == GDK_Insert) {
2035 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2036 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2038 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2041 GString* keycmd = g_string_new(uzbl.state.keycmd);
2042 g_string_append (keycmd, str);
2043 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2050 if (event->keyval == GDK_BackSpace)
2051 keycmd_bs(NULL, NULL, NULL);
2053 gboolean key_ret = FALSE;
2054 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2057 GString* keycmd = g_string_new(uzbl.state.keycmd);
2058 g_string_append(keycmd, event->string);
2059 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2062 run_keycmd(key_ret);
2064 if (key_ret) return (!uzbl.behave.insert_mode);
2069 run_keycmd(const gboolean key_ret) {
2070 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2072 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2074 parse_command(act->name, act->param, NULL);
2078 /* try if it's an incremental keycmd or one that takes args, and run it */
2079 GString* short_keys = g_string_new ("");
2080 GString* short_keys_inc = g_string_new ("");
2082 guint len = strlen(uzbl.state.keycmd);
2083 for (i=0; i<len; i++) {
2084 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2085 g_string_assign(short_keys_inc, short_keys->str);
2086 g_string_append_c(short_keys, '_');
2087 g_string_append_c(short_keys_inc, '*');
2089 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2090 /* run normal cmds only if return was pressed */
2091 exec_paramcmd(act, i);
2094 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2095 if (key_ret) /* just quit the incremental command on return */
2097 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2101 g_string_truncate(short_keys, short_keys->len - 1);
2103 g_string_free (short_keys, TRUE);
2104 g_string_free (short_keys_inc, TRUE);
2108 exec_paramcmd(const Action *act, const guint i) {
2109 GString *parampart = g_string_new (uzbl.state.keycmd);
2110 GString *actionname = g_string_new ("");
2111 GString *actionparam = g_string_new ("");
2112 g_string_erase (parampart, 0, i+1);
2114 g_string_printf (actionname, act->name, parampart->str);
2116 g_string_printf (actionparam, act->param, parampart->str);
2117 parse_command(actionname->str, actionparam->str, NULL);
2118 g_string_free(actionname, TRUE);
2119 g_string_free(actionparam, TRUE);
2120 g_string_free(parampart, TRUE);
2128 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2129 //main_window_ref = g_object_ref(scrolled_window);
2130 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
2132 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2133 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2135 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2136 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2137 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2138 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2139 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2140 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2141 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2142 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2143 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2144 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2145 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2147 return scrolled_window;
2154 g->mainbar = gtk_hbox_new (FALSE, 0);
2156 /* keep a reference to the bar so we can re-pack it at runtime*/
2157 //sbar_ref = g_object_ref(g->mainbar);
2159 g->mainbar_label = gtk_label_new ("");
2160 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2161 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2162 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2163 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2164 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2165 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2170 GtkWidget* create_window () {
2171 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2172 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2173 gtk_widget_set_name (window, "Uzbl browser");
2174 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2175 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2181 GtkPlug* create_plug () {
2182 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2183 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2184 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2191 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2193 If actname is one that calls an external command, this function will inject
2194 newargs in front of the user-provided args in that command line. They will
2195 come become after the body of the script (in sh) or after the name of
2196 the command to execute (in spawn).
2197 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2198 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2200 The return value consist of two strings: the action (sh, ...) and its args.
2202 If act is not one that calls an external command, then the given action merely
2205 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2206 gchar *actdup = g_strdup(actname);
2207 g_array_append_val(rets, actdup);
2209 if ((g_strcmp0(actname, "spawn") == 0) ||
2210 (g_strcmp0(actname, "sh") == 0) ||
2211 (g_strcmp0(actname, "sync_spawn") == 0) ||
2212 (g_strcmp0(actname, "sync_sh") == 0)) {
2214 GString *a = g_string_new("");
2215 gchar **spawnparts = split_quoted(origargs, FALSE);
2216 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2217 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2219 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2220 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2222 g_array_append_val(rets, a->str);
2223 g_string_free(a, FALSE);
2224 g_strfreev(spawnparts);
2226 gchar *origdup = g_strdup(origargs);
2227 g_array_append_val(rets, origdup);
2229 return (gchar**)g_array_free(rets, FALSE);
2233 run_handler (const gchar *act, const gchar *args) {
2234 /* Consider this code a temporary hack to make the handlers usable.
2235 In practice, all this splicing, injection, and reconstruction is
2236 inefficient, annoying and hard to manage. Potential pitfalls arise
2237 when the handler specific args 1) are not quoted (the handler
2238 callbacks should take care of this) 2) are quoted but interfere
2239 with the users' own quotation. A more ideal solution is
2240 to refactor parse_command so that it doesn't just take a string
2241 and execute it; rather than that, we should have a function which
2242 returns the argument vector parsed from the string. This vector
2243 could be modified (e.g. insert additional args into it) before
2244 passing it to the next function that actually executes it. Though
2245 it still isn't perfect for chain actions.. will reconsider & re-
2246 factor when I have the time. -duc */
2248 char **parts = g_strsplit(act, " ", 2);
2250 if (g_strcmp0(parts[0], "chain") == 0) {
2251 GString *newargs = g_string_new("");
2252 gchar **chainparts = split_quoted(parts[1], FALSE);
2254 /* for every argument in the chain, inject the handler args
2255 and make sure the new parts are wrapped in quotes */
2256 gchar **cp = chainparts;
2258 gchar *quotless = NULL;
2259 gchar **spliced_quotless = NULL; // sigh -_-;
2260 gchar **inpart = NULL;
2263 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2265 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2266 } else quotless = g_strdup(*cp);
2268 spliced_quotless = g_strsplit(quotless, " ", 2);
2269 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2270 g_strfreev(spliced_quotless);
2272 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2278 parse_command(parts[0], &(newargs->str[1]), NULL);
2279 g_string_free(newargs, TRUE);
2280 g_strfreev(chainparts);
2283 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2284 parse_command(inparts[0], inparts[1], NULL);
2292 add_binding (const gchar *key, const gchar *act) {
2293 char **parts = g_strsplit(act, " ", 2);
2300 if (uzbl.state.verbose)
2301 printf ("Binding %-10s : %s\n", key, act);
2302 action = new_action(parts[0], parts[1]);
2304 if (g_hash_table_remove (uzbl.bindings, key))
2305 g_warning ("Overwriting existing binding for \"%s\"", key);
2306 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2311 get_xdg_var (XDG_Var xdg) {
2312 const gchar* actual_value = getenv (xdg.environmental);
2313 const gchar* home = getenv ("HOME");
2314 gchar* return_value;
2316 if (! actual_value || strcmp (actual_value, "") == 0) {
2317 if (xdg.default_value) {
2318 return_value = str_replace ("~", home, xdg.default_value);
2320 return_value = NULL;
2323 return_value = str_replace("~", home, actual_value);
2326 return return_value;
2330 find_xdg_file (int xdg_type, char* filename) {
2331 /* xdg_type = 0 => config
2332 xdg_type = 1 => data
2333 xdg_type = 2 => cache*/
2335 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2336 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2339 gchar* temporary_string;
2343 if (! file_exists (temporary_file) && xdg_type != 2) {
2344 buf = get_xdg_var (XDG[3 + xdg_type]);
2345 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2348 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2349 g_free (temporary_file);
2350 temporary_file = g_strconcat (temporary_string, filename, NULL);
2354 //g_free (temporary_string); - segfaults.
2356 if (file_exists (temporary_file)) {
2357 return temporary_file;
2364 State *s = &uzbl.state;
2365 Network *n = &uzbl.net;
2367 for (i = 0; default_config[i].command != NULL; i++) {
2368 parse_cmd_line(default_config[i].command, NULL);
2371 if (g_strcmp0(s->config_file, "-") == 0) {
2372 s->config_file = NULL;
2376 else if (!s->config_file) {
2377 s->config_file = find_xdg_file (0, "/uzbl/config");
2380 if (s->config_file) {
2381 GArray* lines = read_file_by_line (s->config_file);
2385 while ((line = g_array_index(lines, gchar*, i))) {
2386 parse_cmd_line (line, NULL);
2390 g_array_free (lines, TRUE);
2392 if (uzbl.state.verbose)
2393 printf ("No configuration file loaded.\n");
2396 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2399 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2402 if (!uzbl.behave.cookie_handler)
2405 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2406 GString *s = g_string_new ("");
2407 SoupURI * soup_uri = soup_message_get_uri(msg);
2408 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2409 run_handler(uzbl.behave.cookie_handler, s->str);
2411 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2412 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2413 if ( p != NULL ) *p = '\0';
2414 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2416 if (uzbl.comm.sync_stdout)
2417 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2419 g_string_free(s, TRUE);
2423 save_cookies (SoupMessage *msg, gpointer user_data){
2427 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2428 cookie = soup_cookie_to_set_cookie_header(ck->data);
2429 SoupURI * soup_uri = soup_message_get_uri(msg);
2430 GString *s = g_string_new ("");
2431 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2432 run_handler(uzbl.behave.cookie_handler, s->str);
2434 g_string_free(s, TRUE);
2439 /* --- WEBINSPECTOR --- */
2441 hide_window_cb(GtkWidget *widget, gpointer data) {
2444 gtk_widget_hide(widget);
2447 static WebKitWebView*
2448 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2451 (void) web_inspector;
2452 GtkWidget* scrolled_window;
2453 GtkWidget* new_web_view;
2456 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2457 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2458 G_CALLBACK(hide_window_cb), NULL);
2460 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2461 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2462 gtk_widget_show(g->inspector_window);
2464 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2465 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2466 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2467 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2468 gtk_widget_show(scrolled_window);
2470 new_web_view = webkit_web_view_new();
2471 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2473 return WEBKIT_WEB_VIEW(new_web_view);
2477 inspector_show_window_cb (WebKitWebInspector* inspector){
2479 gtk_widget_show(uzbl.gui.inspector_window);
2483 /* TODO: Add variables and code to make use of these functions */
2485 inspector_close_window_cb (WebKitWebInspector* inspector){
2491 inspector_attach_window_cb (WebKitWebInspector* inspector){
2497 inspector_detach_window_cb (WebKitWebInspector* inspector){
2503 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2509 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2515 set_up_inspector() {
2517 WebKitWebSettings *settings = view_settings();
2518 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2520 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2521 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2522 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2523 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2524 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2525 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2526 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2528 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2532 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2534 uzbl_cmdprop *c = v;
2539 if(c->type == TYPE_STR)
2540 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2541 else if(c->type == TYPE_INT)
2542 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2546 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2550 printf("bind %s = %s %s\n", (char *)k ,
2551 (char *)a->name, a->param?(char *)a->param:"");
2556 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2557 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2560 /* set up gtk, gobject, variable defaults and other things that tests and other
2561 * external applications need to do anyhow */
2563 initialize(int argc, char *argv[]) {
2564 gtk_init (&argc, &argv);
2565 if (!g_thread_supported ())
2566 g_thread_init (NULL);
2567 uzbl.state.executable_path = g_strdup(argv[0]);
2568 uzbl.state.selected_url = NULL;
2569 uzbl.state.searchtx = NULL;
2571 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2572 g_option_context_add_main_entries (context, entries, NULL);
2573 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2574 g_option_context_parse (context, &argc, &argv, NULL);
2575 g_option_context_free(context);
2577 if (uzbl.behave.print_version) {
2578 printf("Commit: %s\n", COMMIT);
2582 /* initialize hash table */
2583 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2585 uzbl.net.soup_session = webkit_get_default_session();
2586 uzbl.state.keycmd = g_strdup("");
2588 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2589 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2590 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2591 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2592 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2593 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2595 uzbl.gui.sbar.progress_s = g_strdup("=");
2596 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2597 uzbl.gui.sbar.progress_w = 10;
2599 /* HTML mode defaults*/
2600 uzbl.behave.html_buffer = g_string_new("");
2601 uzbl.behave.html_endmarker = g_strdup(".");
2602 uzbl.behave.html_timeout = 60;
2603 uzbl.behave.base_url = g_strdup("http://invalid");
2605 /* default mode indicators */
2606 uzbl.behave.insert_indicator = g_strdup("I");
2607 uzbl.behave.cmd_indicator = g_strdup("C");
2608 set_insert_mode(FALSE);
2610 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2611 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2612 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2613 uzbl.info.arch = ARCH;
2614 uzbl.info.commit = COMMIT;
2617 make_var_to_name_hash();
2619 uzbl.gui.scrolled_win = create_browser();
2622 #ifndef UZBL_LIBRARY
2625 main (int argc, char* argv[]) {
2626 initialize(argc, argv);
2628 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2632 /* initial packing */
2633 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2634 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2636 if (uzbl.state.socket_id) {
2637 uzbl.gui.plug = create_plug ();
2638 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2639 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2641 uzbl.gui.main_window = create_window ();
2642 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2643 gtk_widget_show_all (uzbl.gui.main_window);
2644 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2647 if(!uzbl.state.instance_name)
2648 uzbl.state.instance_name = itos((int)uzbl.xwin);
2650 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2652 if (uzbl.state.verbose) {
2653 printf("Uzbl start location: %s\n", argv[0]);
2654 if (uzbl.state.socket_id)
2655 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2657 printf("window_id %i\n",(int) uzbl.xwin);
2658 printf("pid %i\n", getpid ());
2659 printf("name: %s\n", uzbl.state.instance_name);
2662 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2663 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2664 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2665 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2666 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2668 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2669 gboolean verbose_override = uzbl.state.verbose;
2673 if (!uzbl.behave.show_status)
2674 gtk_widget_hide(uzbl.gui.mainbar);
2681 if (verbose_override > uzbl.state.verbose)
2682 uzbl.state.verbose = verbose_override;
2685 set_var_value("uri", uri_override);
2686 g_free(uri_override);
2687 } else if (uzbl.state.uri)
2688 cmd_load_uri(uzbl.gui.web_view, NULL);
2693 return EXIT_SUCCESS;
2697 /* vi: set et ts=4: */