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 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
79 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
81 "Print the version and exit", NULL },
82 { NULL, 0, 0, 0, NULL, NULL, NULL }
85 /* associate command names to their properties */
86 typedef const struct {
94 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
96 /* abbreviations to help keep the table's width humane */
97 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
98 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
103 } var_name_to_ptr[] = {
104 /* variable name pointer to variable in code type dump callback function */
105 /* ---------------------------------------------------------------------------------------------- */
106 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
107 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
108 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
109 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
110 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
111 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
112 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
113 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
114 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
115 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
116 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
117 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
118 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
119 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
120 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
121 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
122 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_title)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
124 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
125 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
126 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
127 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
128 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
129 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
130 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
131 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
132 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
133 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
134 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
135 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
136 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
137 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
138 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
139 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
140 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
141 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
142 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
143 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
144 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
145 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
147 /* exported WebKitWebSettings properties */
148 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
149 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
150 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
151 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
152 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
153 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
154 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
155 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
156 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
157 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
158 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
159 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
160 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
161 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
162 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
163 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
165 /* constants (not dumpable or writeable) */
166 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
167 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
168 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
169 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
170 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
171 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
172 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
173 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
174 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
175 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
176 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
178 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
179 }, *n2v_p = var_name_to_ptr;
186 { "SHIFT", GDK_SHIFT_MASK }, // shift
187 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
188 { "CONTROL", GDK_CONTROL_MASK }, // control
189 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
190 { "MOD2", GDK_MOD2_MASK }, // 5th mod
191 { "MOD3", GDK_MOD3_MASK }, // 6th mod
192 { "MOD4", GDK_MOD4_MASK }, // 7th mod
193 { "MOD5", GDK_MOD5_MASK }, // 8th mod
194 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
195 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
196 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
197 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
198 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
199 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
200 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
201 { "META", GDK_META_MASK }, // meta (since 2.10)
206 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
207 * for quick access */
209 make_var_to_name_hash() {
210 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
212 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
217 /* --- UTILITY FUNCTIONS --- */
219 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
221 get_exp_type(gchar *s) {
225 else if(*(s+1) == '{')
226 return EXP_BRACED_VAR;
227 else if(*(s+1) == '<')
229 else if(*(s+1) == '[')
232 return EXP_SIMPLE_VAR;
238 * recurse == 1: don't expand '@(command)@'
239 * recurse == 2: don't expand '@<java script>@'
242 expand(char *s, guint recurse) {
246 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
251 gchar *cmd_stdout = NULL;
253 GString *buf = g_string_new("");
254 GString *js_ret = g_string_new("");
259 g_string_append_c(buf, *++s);
264 etype = get_exp_type(s);
269 vend = strpbrk(s, end_simple_var);
270 if(!vend) vend = strchr(s, '\0');
274 vend = strchr(s, upto);
275 if(!vend) vend = strchr(s, '\0');
279 strcpy(str_end, ")@");
281 vend = strstr(s, str_end);
282 if(!vend) vend = strchr(s, '\0');
286 strcpy(str_end, ">@");
288 vend = strstr(s, str_end);
289 if(!vend) vend = strchr(s, '\0');
293 strcpy(str_end, "]@");
295 vend = strstr(s, str_end);
296 if(!vend) vend = strchr(s, '\0');
301 strncpy(ret, s, vend-s);
305 if(etype == EXP_SIMPLE_VAR ||
306 etype == EXP_BRACED_VAR) {
307 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
308 if(c->type == TYPE_STR && *c->ptr != NULL) {
309 g_string_append(buf, (gchar *)*c->ptr);
310 } else if(c->type == TYPE_INT) {
311 char *b = itos((uintptr_t)*c->ptr);
312 g_string_append(buf, b);
317 if(etype == EXP_SIMPLE_VAR)
322 else if(recurse != 1 &&
324 mycmd = expand(ret, 1);
325 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
329 g_printerr("error on running command: %s\n", err->message);
332 else if (*cmd_stdout) {
333 int len = strlen(cmd_stdout);
335 if(cmd_stdout[len-1] == '\n')
336 cmd_stdout[--len] = 0; /* strip trailing newline */
338 g_string_append(buf, cmd_stdout);
343 else if(recurse != 2 &&
345 mycmd = expand(ret, 2);
346 eval_js(uzbl.gui.web_view, mycmd, js_ret);
350 g_string_append(buf, js_ret->str);
351 g_string_free(js_ret, TRUE);
352 js_ret = g_string_new("");
356 else if(etype == EXP_ESCAPE) {
357 mycmd = expand(ret, 0);
358 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
360 g_string_append(buf, escaped);
369 g_string_append_c(buf, *s);
374 g_string_free(js_ret, TRUE);
375 return g_string_free(buf, FALSE);
382 snprintf(tmp, sizeof(tmp), "%i", val);
383 return g_strdup(tmp);
387 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
390 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
393 str_replace (const char* search, const char* replace, const char* string) {
397 buf = g_strsplit (string, search, -1);
398 ret = g_strjoinv (replace, buf);
399 g_strfreev(buf); // somebody said this segfaults
405 read_file_by_line (gchar *path) {
406 GIOChannel *chan = NULL;
407 gchar *readbuf = NULL;
409 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
412 chan = g_io_channel_new_file(path, "r", NULL);
415 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
416 const gchar* val = g_strdup (readbuf);
417 g_array_append_val (lines, val);
422 g_io_channel_unref (chan);
424 fprintf(stderr, "File '%s' not be read.\n", path);
431 gchar* parseenv (char* string) {
432 extern char** environ;
433 gchar* tmpstr = NULL;
437 while (environ[i] != NULL) {
438 gchar** env = g_strsplit (environ[i], "=", 2);
439 gchar* envname = g_strconcat ("$", env[0], NULL);
441 if (g_strrstr (string, envname) != NULL) {
442 tmpstr = g_strdup(string);
444 string = str_replace(envname, env[1], tmpstr);
449 g_strfreev (env); // somebody said this breaks uzbl
457 setup_signal(int signr, sigfunc *shandler) {
458 struct sigaction nh, oh;
460 nh.sa_handler = shandler;
461 sigemptyset(&nh.sa_mask);
464 if(sigaction(signr, &nh, &oh) < 0)
472 if (uzbl.behave.fifo_dir)
473 unlink (uzbl.comm.fifo_path);
474 if (uzbl.behave.socket_dir)
475 unlink (uzbl.comm.socket_path);
477 g_free(uzbl.state.executable_path);
478 g_free(uzbl.state.keycmd);
479 g_hash_table_destroy(uzbl.bindings);
480 g_hash_table_destroy(uzbl.behave.commands);
483 /* used for html_mode_timeout
484 * be sure to extend this function to use
485 * more timers if needed in other places
488 set_timeout(int seconds) {
490 memset(&t, 0, sizeof t);
492 t.it_value.tv_sec = seconds;
493 t.it_value.tv_usec = 0;
494 setitimer(ITIMER_REAL, &t, NULL);
497 /* --- SIGNAL HANDLER --- */
500 catch_sigterm(int s) {
506 catch_sigint(int s) {
516 set_var_value("mode", "0");
521 /* --- CALLBACKS --- */
524 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527 (void) navigation_action;
528 (void) policy_decision;
530 const gchar* uri = webkit_network_request_get_uri (request);
531 if (uzbl.state.verbose)
532 printf("New window requested -> %s \n", uri);
533 webkit_web_policy_decision_use(policy_decision);
538 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
543 /* If we can display it, let's display it... */
544 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
545 webkit_web_policy_decision_use (policy_decision);
549 /* ...everything we can't displayed is downloaded */
550 webkit_web_policy_decision_download (policy_decision);
555 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
559 if (uzbl.state.selected_url != NULL) {
560 if (uzbl.state.verbose)
561 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
562 new_window_load_uri(uzbl.state.selected_url);
564 if (uzbl.state.verbose)
565 printf("New web view -> %s\n","Nothing to open, exiting");
571 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
574 if (uzbl.behave.download_handler) {
575 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
576 if (uzbl.state.verbose)
577 printf("Download -> %s\n",uri);
578 /* if urls not escaped, we may have to escape and quote uri before this call */
579 run_handler(uzbl.behave.download_handler, uri);
584 /* scroll a bar in a given direction */
586 scroll (GtkAdjustment* bar, GArray *argv) {
590 gdouble page_size = gtk_adjustment_get_page_size(bar);
591 gdouble value = gtk_adjustment_get_value(bar);
592 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
595 value += page_size * amount * 0.01;
599 max_value = gtk_adjustment_get_upper(bar) - page_size;
601 if (value > max_value)
602 value = max_value; /* don't scroll past the end of the page */
604 gtk_adjustment_set_value (bar, value);
608 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
609 (void) page; (void) argv; (void) result;
610 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
614 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
615 (void) page; (void) argv; (void) result;
616 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
617 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
621 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
622 (void) page; (void) result;
623 scroll(uzbl.gui.bar_v, argv);
627 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
628 (void) page; (void) result;
629 scroll(uzbl.gui.bar_h, argv);
634 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
635 if(uzbl.state.verbose)
636 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
638 /* update geometry var with the actual geometry
639 this is necessary as some WMs don't seem to honour
640 the above setting and we don't want to end up with
641 wrong geometry information
648 if (!uzbl.behave.show_status) {
649 gtk_widget_hide(uzbl.gui.mainbar);
651 gtk_widget_show(uzbl.gui.mainbar);
657 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
662 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
666 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
671 if (uzbl.behave.show_status) {
672 gtk_widget_hide(uzbl.gui.mainbar);
674 gtk_widget_show(uzbl.gui.mainbar);
676 uzbl.behave.show_status = !uzbl.behave.show_status;
681 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
685 //Set selected_url state variable
686 g_free(uzbl.state.selected_url);
687 uzbl.state.selected_url = NULL;
689 uzbl.state.selected_url = g_strdup(link);
695 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
698 const gchar *title = webkit_web_view_get_title(web_view);
699 if (uzbl.gui.main_title)
700 g_free (uzbl.gui.main_title);
701 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
706 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
709 uzbl.gui.sbar.load_progress = progress;
711 g_free(uzbl.gui.sbar.progress_bar);
712 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
718 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
722 if (uzbl.behave.load_finish_handler)
723 run_handler(uzbl.behave.load_finish_handler, "");
726 void clear_keycmd() {
727 g_free(uzbl.state.keycmd);
728 uzbl.state.keycmd = g_strdup("");
732 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
736 uzbl.gui.sbar.load_progress = 0;
737 clear_keycmd(); // don't need old commands to remain on new page?
738 if (uzbl.behave.load_start_handler)
739 run_handler(uzbl.behave.load_start_handler, "");
743 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
746 g_free (uzbl.state.uri);
747 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
748 uzbl.state.uri = g_string_free (newuri, FALSE);
749 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
750 set_insert_mode(uzbl.behave.always_insert_mode);
753 if (uzbl.behave.load_commit_handler)
754 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
758 destroy_cb (GtkWidget* widget, gpointer data) {
766 if (uzbl.behave.history_handler) {
768 struct tm * timeinfo;
771 timeinfo = localtime ( &rawtime );
772 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
773 run_handler(uzbl.behave.history_handler, date);
778 /* VIEW funcs (little webkit wrappers) */
779 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
781 VIEWFUNC(reload_bypass_cache)
782 VIEWFUNC(stop_loading)
789 /* -- command to callback/function map for things we cannot attach to any signals */
790 static struct {char *key; CommandInfo value;} cmdlist[] =
791 { /* key function no_split */
792 { "back", {view_go_back, 0} },
793 { "forward", {view_go_forward, 0} },
794 { "scroll_vert", {scroll_vert, 0} },
795 { "scroll_horz", {scroll_horz, 0} },
796 { "scroll_begin", {scroll_begin, 0} },
797 { "scroll_end", {scroll_end, 0} },
798 { "reload", {view_reload, 0}, },
799 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
800 { "stop", {view_stop_loading, 0}, },
801 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
802 { "zoom_out", {view_zoom_out, 0}, },
803 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
804 { "uri", {load_uri, TRUE} },
805 { "js", {run_js, TRUE} },
806 { "script", {run_external_js, 0} },
807 { "toggle_status", {toggle_status_cb, 0} },
808 { "spawn", {spawn, 0} },
809 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
810 { "sh", {spawn_sh, 0} },
811 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
812 { "exit", {close_uzbl, 0} },
813 { "search", {search_forward_text, TRUE} },
814 { "search_reverse", {search_reverse_text, TRUE} },
815 { "dehilight", {dehilight, 0} },
816 { "toggle_insert_mode", {toggle_insert_mode, 0} },
817 { "set", {set_var, TRUE} },
818 //{ "get", {get_var, TRUE} },
819 { "bind", {act_bind, TRUE} },
820 { "dump_config", {act_dump_config, 0} },
821 { "keycmd", {keycmd, TRUE} },
822 { "keycmd_nl", {keycmd_nl, TRUE} },
823 { "keycmd_bs", {keycmd_bs, 0} },
824 { "chain", {chain, 0} },
825 { "print", {print, TRUE} }
832 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
834 for (i = 0; i < LENGTH(cmdlist); i++)
835 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
838 /* -- CORE FUNCTIONS -- */
841 free_action(gpointer act) {
842 Action *action = (Action*)act;
843 g_free(action->name);
845 g_free(action->param);
850 new_action(const gchar *name, const gchar *param) {
851 Action *action = g_new(Action, 1);
853 action->name = g_strdup(name);
855 action->param = g_strdup(param);
857 action->param = NULL;
863 file_exists (const char * filename) {
864 return (access(filename, F_OK) == 0);
868 set_var(WebKitWebView *page, GArray *argv, GString *result) {
869 (void) page; (void) result;
870 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
871 if (split[0] != NULL) {
872 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
873 set_var_value(g_strstrip(split[0]), value);
880 print(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) result;
884 buf = expand(argv_idx(argv, 0), 0);
885 g_string_assign(result, buf);
890 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
891 (void) page; (void) result;
892 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
893 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
894 add_binding(g_strstrip(split[0]), value);
910 void set_mode_indicator() {
911 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
912 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
915 void set_insert_mode(gboolean mode) {
916 uzbl.behave.insert_mode = mode;
917 set_mode_indicator();
921 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
922 (void) page; (void) result;
924 if (argv_idx(argv, 0)) {
925 if (strcmp (argv_idx(argv, 0), "0") == 0) {
926 set_insert_mode(FALSE);
928 set_insert_mode(TRUE);
931 set_insert_mode( !uzbl.behave.insert_mode );
938 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
941 if (argv_idx(argv, 0)) {
942 GString* newuri = g_string_new (argv_idx(argv, 0));
943 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
944 run_js(web_view, argv, NULL);
947 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
948 g_string_prepend (newuri, "http://");
949 /* if we do handle cookies, ask our handler for them */
950 webkit_web_view_load_uri (web_view, newuri->str);
951 g_string_free (newuri, TRUE);
959 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
960 size_t argumentCount, const JSValueRef arguments[],
961 JSValueRef* exception) {
966 JSStringRef js_result_string;
967 GString *result = g_string_new("");
969 if (argumentCount >= 1) {
970 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
971 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
972 char ctl_line[arg_size];
973 JSStringGetUTF8CString(arg, ctl_line, arg_size);
975 parse_cmd_line(ctl_line, result);
977 JSStringRelease(arg);
979 js_result_string = JSStringCreateWithUTF8CString(result->str);
981 g_string_free(result, TRUE);
983 return JSValueMakeString(ctx, js_result_string);
986 static JSStaticFunction js_static_functions[] = {
987 {"run", js_run_command, kJSPropertyAttributeNone},
992 /* This function creates the class and its definition, only once */
993 if (!uzbl.js.initialized) {
994 /* it would be pretty cool to make this dynamic */
995 uzbl.js.classdef = kJSClassDefinitionEmpty;
996 uzbl.js.classdef.staticFunctions = js_static_functions;
998 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1004 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1005 WebKitWebFrame *frame;
1006 JSGlobalContextRef context;
1007 JSObjectRef globalobject;
1008 JSStringRef var_name;
1010 JSStringRef js_script;
1011 JSValueRef js_result;
1012 JSStringRef js_result_string;
1013 size_t js_result_size;
1017 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1018 context = webkit_web_frame_get_global_context(frame);
1019 globalobject = JSContextGetGlobalObject(context);
1021 /* uzbl javascript namespace */
1022 var_name = JSStringCreateWithUTF8CString("Uzbl");
1023 JSObjectSetProperty(context, globalobject, var_name,
1024 JSObjectMake(context, uzbl.js.classref, NULL),
1025 kJSClassAttributeNone, NULL);
1027 /* evaluate the script and get return value*/
1028 js_script = JSStringCreateWithUTF8CString(script);
1029 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1030 if (js_result && !JSValueIsUndefined(context, js_result)) {
1031 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1032 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1034 if (js_result_size) {
1035 char js_result_utf8[js_result_size];
1036 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1037 g_string_assign(result, js_result_utf8);
1040 JSStringRelease(js_result_string);
1044 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1046 JSStringRelease(var_name);
1047 JSStringRelease(js_script);
1051 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1053 if (argv_idx(argv, 0))
1054 eval_js(web_view, argv_idx(argv, 0), result);
1058 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1060 if (argv_idx(argv, 0)) {
1061 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1066 while ((line = g_array_index(lines, gchar*, i))) {
1068 js = g_strdup (line);
1070 gchar* newjs = g_strconcat (js, line, NULL);
1077 if (uzbl.state.verbose)
1078 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1080 if (argv_idx (argv, 1)) {
1081 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1085 eval_js (web_view, js, result);
1087 g_array_free (lines, TRUE);
1092 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1093 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1094 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1095 webkit_web_view_unmark_text_matches (page);
1096 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1097 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1101 if (uzbl.state.searchtx) {
1102 if (uzbl.state.verbose)
1103 printf ("Searching: %s\n", uzbl.state.searchtx);
1104 webkit_web_view_set_highlight_text_matches (page, TRUE);
1105 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1110 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1112 search_text(page, argv, TRUE);
1116 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1118 search_text(page, argv, FALSE);
1122 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1123 (void) argv; (void) result;
1124 webkit_web_view_set_highlight_text_matches (page, FALSE);
1129 new_window_load_uri (const gchar * uri) {
1130 if (uzbl.behave.new_window) {
1131 GString *s = g_string_new ("");
1132 g_string_printf(s, "'%s'", uri);
1133 run_handler(uzbl.behave.new_window, s->str);
1136 GString* to_execute = g_string_new ("");
1137 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1139 for (i = 0; entries[i].long_name != NULL; i++) {
1140 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1141 gchar** str = (gchar**)entries[i].arg_data;
1143 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1147 if (uzbl.state.verbose)
1148 printf("\n%s\n", to_execute->str);
1149 g_spawn_command_line_async (to_execute->str, NULL);
1150 g_string_free (to_execute, TRUE);
1154 chain (WebKitWebView *page, GArray *argv, GString *result) {
1155 (void) page; (void) result;
1157 gchar **parts = NULL;
1159 while ((a = argv_idx(argv, i++))) {
1160 parts = g_strsplit (a, " ", 2);
1161 parse_command(parts[0], parts[1], result);
1167 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1171 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1177 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1181 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1187 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1192 int len = strlen(uzbl.state.keycmd);
1193 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1195 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1200 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1207 /* --Statusbar functions-- */
1209 build_progressbar_ascii(int percent) {
1210 int width=uzbl.gui.sbar.progress_w;
1213 GString *bar = g_string_new("");
1215 l = (double)percent*((double)width/100.);
1216 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1218 for(i=0; i<(int)l; i++)
1219 g_string_append(bar, uzbl.gui.sbar.progress_s);
1222 g_string_append(bar, uzbl.gui.sbar.progress_u);
1224 return g_string_free(bar, FALSE);
1226 /* --End Statusbar functions-- */
1229 sharg_append(GArray *a, const gchar *str) {
1230 const gchar *s = (str ? str : "");
1231 g_array_append_val(a, s);
1234 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1236 run_command (const gchar *command, const guint npre, const gchar **args,
1237 const gboolean sync, char **output_stdout) {
1238 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1241 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1242 gchar *pid = itos(getpid());
1243 gchar *xwin = itos(uzbl.xwin);
1245 sharg_append(a, command);
1246 for (i = 0; i < npre; i++) /* add n args before the default vars */
1247 sharg_append(a, args[i]);
1248 sharg_append(a, uzbl.state.config_file);
1249 sharg_append(a, pid);
1250 sharg_append(a, xwin);
1251 sharg_append(a, uzbl.comm.fifo_path);
1252 sharg_append(a, uzbl.comm.socket_path);
1253 sharg_append(a, uzbl.state.uri);
1254 sharg_append(a, uzbl.gui.main_title);
1256 for (i = npre; i < g_strv_length((gchar**)args); i++)
1257 sharg_append(a, args[i]);
1261 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1263 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1264 NULL, NULL, output_stdout, NULL, NULL, &err);
1265 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1266 NULL, NULL, NULL, &err);
1268 if (uzbl.state.verbose) {
1269 GString *s = g_string_new("spawned:");
1270 for (i = 0; i < (a->len); i++) {
1271 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1272 g_string_append_printf(s, " %s", qarg);
1275 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1276 printf("%s\n", s->str);
1277 g_string_free(s, TRUE);
1279 printf("Stdout: %s\n", *output_stdout);
1283 g_printerr("error on run_command: %s\n", err->message);
1288 g_array_free (a, TRUE);
1293 split_quoted(const gchar* src, const gboolean unquote) {
1294 /* split on unquoted space, return array of strings;
1295 remove a layer of quotes and backslashes if unquote */
1296 if (!src) return NULL;
1298 gboolean dq = FALSE;
1299 gboolean sq = FALSE;
1300 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1301 GString *s = g_string_new ("");
1305 for (p = src; *p != '\0'; p++) {
1306 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1307 else if (*p == '\\') { g_string_append_c(s, *p++);
1308 g_string_append_c(s, *p); }
1309 else if ((*p == '"') && unquote && !sq) dq = !dq;
1310 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1312 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1313 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1315 else if ((*p == ' ') && !dq && !sq) {
1316 dup = g_strdup(s->str);
1317 g_array_append_val(a, dup);
1318 g_string_truncate(s, 0);
1319 } else g_string_append_c(s, *p);
1321 dup = g_strdup(s->str);
1322 g_array_append_val(a, dup);
1323 ret = (gchar**)a->data;
1324 g_array_free (a, FALSE);
1325 g_string_free (s, TRUE);
1330 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1331 (void)web_view; (void)result;
1332 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1333 if (argv_idx(argv, 0))
1334 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1338 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1339 (void)web_view; (void)result;
1341 if (argv_idx(argv, 0))
1342 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1343 TRUE, &uzbl.comm.sync_stdout);
1347 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1348 (void)web_view; (void)result;
1349 if (!uzbl.behave.shell_cmd) {
1350 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1355 gchar *spacer = g_strdup("");
1356 g_array_insert_val(argv, 1, spacer);
1357 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1359 for (i = 1; i < g_strv_length(cmd); i++)
1360 g_array_prepend_val(argv, cmd[i]);
1362 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1368 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1369 (void)web_view; (void)result;
1370 if (!uzbl.behave.shell_cmd) {
1371 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1376 gchar *spacer = g_strdup("");
1377 g_array_insert_val(argv, 1, spacer);
1378 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1380 for (i = 1; i < g_strv_length(cmd); i++)
1381 g_array_prepend_val(argv, cmd[i]);
1383 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1384 TRUE, &uzbl.comm.sync_stdout);
1390 parse_command(const char *cmd, const char *param, GString *result) {
1393 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1395 gchar **par = split_quoted(param, TRUE);
1396 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1398 if (c->no_split) { /* don't split */
1399 sharg_append(a, param);
1401 for (i = 0; i < g_strv_length(par); i++)
1402 sharg_append(a, par[i]);
1405 if (result == NULL) {
1406 GString *result_print = g_string_new("");
1408 c->function(uzbl.gui.web_view, a, result_print);
1409 if (result_print->len)
1410 printf("%*s\n", result_print->len, result_print->str);
1412 g_string_free(result_print, TRUE);
1414 c->function(uzbl.gui.web_view, a, result);
1417 g_array_free (a, TRUE);
1420 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1427 if(*uzbl.net.proxy_url == ' '
1428 || uzbl.net.proxy_url == NULL) {
1429 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1430 (GType) SOUP_SESSION_PROXY_URI);
1433 suri = soup_uri_new(uzbl.net.proxy_url);
1434 g_object_set(G_OBJECT(uzbl.net.soup_session),
1435 SOUP_SESSION_PROXY_URI,
1437 soup_uri_free(suri);
1444 if(file_exists(uzbl.gui.icon)) {
1445 if (uzbl.gui.main_window)
1446 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1448 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1454 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1455 g_array_append_val (a, uzbl.state.uri);
1456 load_uri(uzbl.gui.web_view, a, NULL);
1457 g_array_free (a, TRUE);
1461 cmd_always_insert_mode() {
1462 set_insert_mode(uzbl.behave.always_insert_mode);
1468 g_object_set(G_OBJECT(uzbl.net.soup_session),
1469 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1473 cmd_max_conns_host() {
1474 g_object_set(G_OBJECT(uzbl.net.soup_session),
1475 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1480 soup_session_remove_feature
1481 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1482 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1483 /*g_free(uzbl.net.soup_logger);*/
1485 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1486 soup_session_add_feature(uzbl.net.soup_session,
1487 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1490 static WebKitWebSettings*
1492 return webkit_web_view_get_settings(uzbl.gui.web_view);
1497 WebKitWebSettings *ws = view_settings();
1498 if (uzbl.behave.font_size > 0) {
1499 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1502 if (uzbl.behave.monospace_size > 0) {
1503 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1504 uzbl.behave.monospace_size, NULL);
1506 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1507 uzbl.behave.font_size, NULL);
1513 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1517 cmd_disable_plugins() {
1518 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1519 !uzbl.behave.disable_plugins, NULL);
1523 cmd_disable_scripts() {
1524 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1525 !uzbl.behave.disable_scripts, NULL);
1529 cmd_minimum_font_size() {
1530 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1531 uzbl.behave.minimum_font_size, NULL);
1534 cmd_autoload_img() {
1535 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1536 uzbl.behave.autoload_img, NULL);
1541 cmd_autoshrink_img() {
1542 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1543 uzbl.behave.autoshrink_img, NULL);
1548 cmd_enable_spellcheck() {
1549 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1550 uzbl.behave.enable_spellcheck, NULL);
1554 cmd_enable_private() {
1555 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1556 uzbl.behave.enable_private, NULL);
1561 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1562 uzbl.behave.print_bg, NULL);
1567 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1568 uzbl.behave.style_uri, NULL);
1572 cmd_resizable_txt() {
1573 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1574 uzbl.behave.resizable_txt, NULL);
1578 cmd_default_encoding() {
1579 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1580 uzbl.behave.default_encoding, NULL);
1584 cmd_enforce_96dpi() {
1585 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1586 uzbl.behave.enforce_96dpi, NULL);
1590 cmd_caret_browsing() {
1591 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1592 uzbl.behave.caret_browsing, NULL);
1596 cmd_cookie_handler() {
1597 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1598 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1599 if ((g_strcmp0(split[0], "sh") == 0) ||
1600 (g_strcmp0(split[0], "spawn") == 0)) {
1601 g_free (uzbl.behave.cookie_handler);
1602 uzbl.behave.cookie_handler =
1603 g_strdup_printf("sync_%s %s", split[0], split[1]);
1610 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1611 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1612 if ((g_strcmp0(split[0], "sh") == 0) ||
1613 (g_strcmp0(split[0], "spawn") == 0)) {
1614 g_free (uzbl.behave.new_window);
1615 uzbl.behave.new_window =
1616 g_strdup_printf("%s %s", split[0], split[1]);
1623 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1628 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1633 if(uzbl.behave.inject_html) {
1634 webkit_web_view_load_html_string (uzbl.gui.web_view,
1635 uzbl.behave.inject_html, NULL);
1644 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1645 uzbl.behave.modmask = 0;
1647 if(uzbl.behave.modkey)
1648 g_free(uzbl.behave.modkey);
1649 uzbl.behave.modkey = buf;
1651 for (i = 0; modkeys[i].key != NULL; i++) {
1652 if (g_strrstr(buf, modkeys[i].key))
1653 uzbl.behave.modmask |= modkeys[i].mask;
1659 if (*uzbl.net.useragent == ' ') {
1660 g_free (uzbl.net.useragent);
1661 uzbl.net.useragent = NULL;
1663 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1664 uzbl.net.useragent, NULL);
1670 if (!uzbl.gui.scrolled_win &&
1674 gtk_widget_ref(uzbl.gui.scrolled_win);
1675 gtk_widget_ref(uzbl.gui.mainbar);
1676 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1677 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1679 if(uzbl.behave.status_top) {
1680 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1681 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1684 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1685 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1687 gtk_widget_unref(uzbl.gui.scrolled_win);
1688 gtk_widget_unref(uzbl.gui.mainbar);
1689 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1694 set_var_value(gchar *name, gchar *val) {
1695 uzbl_cmdprop *c = NULL;
1699 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1700 if(!c->writeable) return TRUE;
1702 /* check for the variable type */
1703 if (c->type == TYPE_STR) {
1704 buf = expand(val, 0);
1707 } else if(c->type == TYPE_INT) {
1708 int *ip = (int *)c->ptr;
1709 buf = expand(val, 0);
1710 *ip = (int)strtoul(buf, &endp, 10);
1712 } else if (c->type == TYPE_FLOAT) {
1713 float *fp = (float *)c->ptr;
1714 buf = expand(val, 0);
1715 *fp = strtod(buf, &endp);
1719 /* invoke a command specific function */
1720 if(c->func) c->func();
1727 Behaviour *b = &uzbl.behave;
1729 if(b->html_buffer->str) {
1730 webkit_web_view_load_html_string (uzbl.gui.web_view,
1731 b->html_buffer->str, b->base_url);
1732 g_string_free(b->html_buffer, TRUE);
1733 b->html_buffer = g_string_new("");
1737 enum {M_CMD, M_HTML};
1739 parse_cmd_line(const char *ctl_line, GString *result) {
1740 Behaviour *b = &uzbl.behave;
1743 if(b->mode == M_HTML) {
1744 len = strlen(b->html_endmarker);
1745 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1746 if(len == strlen(ctl_line)-1 &&
1747 !strncmp(b->html_endmarker, ctl_line, len)) {
1749 set_var_value("mode", "0");
1754 set_timeout(b->html_timeout);
1755 g_string_append(b->html_buffer, ctl_line);
1758 else if((ctl_line[0] == '#') /* Comments */
1759 || (ctl_line[0] == ' ')
1760 || (ctl_line[0] == '\n'))
1761 ; /* ignore these lines */
1762 else { /* parse a command */
1764 gchar **tokens = NULL;
1765 len = strlen(ctl_line);
1767 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1768 ctlstrip = g_strndup(ctl_line, len - 1);
1769 else ctlstrip = g_strdup(ctl_line);
1771 tokens = g_strsplit(ctlstrip, " ", 2);
1772 parse_command(tokens[0], tokens[1], result);
1779 build_stream_name(int type, const gchar* dir) {
1780 State *s = &uzbl.state;
1784 str = g_strdup_printf
1785 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1786 } else if (type == SOCKET) {
1787 str = g_strdup_printf
1788 ("%s/uzbl_socket_%s", dir, s->instance_name);
1794 control_fifo(GIOChannel *gio, GIOCondition condition) {
1795 if (uzbl.state.verbose)
1796 printf("triggered\n");
1801 if (condition & G_IO_HUP)
1802 g_error ("Fifo: Read end of pipe died!\n");
1805 g_error ("Fifo: GIOChannel broke\n");
1807 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1808 if (ret == G_IO_STATUS_ERROR) {
1809 g_error ("Fifo: Error reading: %s\n", err->message);
1813 parse_cmd_line(ctl_line, NULL);
1820 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1821 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1822 if (unlink(uzbl.comm.fifo_path) == -1)
1823 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1824 g_free(uzbl.comm.fifo_path);
1825 uzbl.comm.fifo_path = NULL;
1828 if (*dir == ' ') { /* space unsets the variable */
1833 GIOChannel *chan = NULL;
1834 GError *error = NULL;
1835 gchar *path = build_stream_name(FIFO, dir);
1837 if (!file_exists(path)) {
1838 if (mkfifo (path, 0666) == 0) {
1839 // 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.
1840 chan = g_io_channel_new_file(path, "r+", &error);
1842 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1843 if (uzbl.state.verbose)
1844 printf ("init_fifo: created successfully as %s\n", path);
1845 uzbl.comm.fifo_path = path;
1847 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1848 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1849 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1850 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1852 /* if we got this far, there was an error; cleanup */
1853 if (error) g_error_free (error);
1860 control_stdin(GIOChannel *gio, GIOCondition condition) {
1862 gchar *ctl_line = NULL;
1865 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1866 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1869 parse_cmd_line(ctl_line, NULL);
1877 GIOChannel *chan = NULL;
1878 GError *error = NULL;
1880 chan = g_io_channel_unix_new(fileno(stdin));
1882 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1883 g_error ("Stdin: could not add watch\n");
1885 if (uzbl.state.verbose)
1886 printf ("Stdin: watch added successfully\n");
1889 g_error ("Stdin: Error while opening: %s\n", error->message);
1891 if (error) g_error_free (error);
1895 control_socket(GIOChannel *chan) {
1896 struct sockaddr_un remote;
1897 unsigned int t = sizeof(remote);
1899 GIOChannel *clientchan;
1901 clientsock = accept (g_io_channel_unix_get_fd(chan),
1902 (struct sockaddr *) &remote, &t);
1904 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1905 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1906 (GIOFunc) control_client_socket, clientchan);
1913 control_client_socket(GIOChannel *clientchan) {
1915 GString *result = g_string_new("");
1916 GError *error = NULL;
1920 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1921 if (ret == G_IO_STATUS_ERROR) {
1922 g_warning ("Error reading: %s\n", error->message);
1923 g_io_channel_shutdown(clientchan, TRUE, &error);
1925 } else if (ret == G_IO_STATUS_EOF) {
1926 /* shutdown and remove channel watch from main loop */
1927 g_io_channel_shutdown(clientchan, TRUE, &error);
1932 parse_cmd_line (ctl_line, result);
1933 g_string_append_c(result, '\n');
1934 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1936 if (ret == G_IO_STATUS_ERROR) {
1937 g_warning ("Error writing: %s", error->message);
1939 g_io_channel_flush(clientchan, &error);
1942 if (error) g_error_free (error);
1943 g_string_free(result, TRUE);
1949 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1950 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1951 if (unlink(uzbl.comm.socket_path) == -1)
1952 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1953 g_free(uzbl.comm.socket_path);
1954 uzbl.comm.socket_path = NULL;
1962 GIOChannel *chan = NULL;
1964 struct sockaddr_un local;
1965 gchar *path = build_stream_name(SOCKET, dir);
1967 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1969 local.sun_family = AF_UNIX;
1970 strcpy (local.sun_path, path);
1971 unlink (local.sun_path);
1973 len = strlen (local.sun_path) + sizeof (local.sun_family);
1974 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1975 if (uzbl.state.verbose)
1976 printf ("init_socket: opened in %s\n", path);
1979 if( (chan = g_io_channel_unix_new(sock)) ) {
1980 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1981 uzbl.comm.socket_path = path;
1984 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1986 /* if we got this far, there was an error; cleanup */
1993 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1994 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1996 // this function may be called very early when the templates are not set (yet), hence the checks
1998 update_title (void) {
1999 Behaviour *b = &uzbl.behave;
2002 if (b->show_status) {
2003 if (b->title_format_short) {
2004 parsed = expand(b->title_format_short, 0);
2005 if (uzbl.gui.main_window)
2006 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2009 if (b->status_format) {
2010 parsed = expand(b->status_format, 0);
2011 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2014 if (b->status_background) {
2016 gdk_color_parse (b->status_background, &color);
2017 //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)
2018 if (uzbl.gui.main_window)
2019 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2020 else if (uzbl.gui.plug)
2021 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2024 if (b->title_format_long) {
2025 parsed = expand(b->title_format_long, 0);
2026 if (uzbl.gui.main_window)
2027 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2034 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2038 retrieve_geometry();
2043 key_press_cb (GtkWidget* window, GdkEventKey* event)
2045 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2049 if (event->type != GDK_KEY_PRESS ||
2050 event->keyval == GDK_Page_Up ||
2051 event->keyval == GDK_Page_Down ||
2052 event->keyval == GDK_Up ||
2053 event->keyval == GDK_Down ||
2054 event->keyval == GDK_Left ||
2055 event->keyval == GDK_Right ||
2056 event->keyval == GDK_Shift_L ||
2057 event->keyval == GDK_Shift_R)
2060 /* turn off insert mode (if always_insert_mode is not used) */
2061 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2062 set_insert_mode(uzbl.behave.always_insert_mode);
2067 if (uzbl.behave.insert_mode &&
2068 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2069 (!uzbl.behave.modmask)
2074 if (event->keyval == GDK_Escape) {
2077 dehilight(uzbl.gui.web_view, NULL, NULL);
2081 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2082 if (event->keyval == GDK_Insert) {
2084 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2085 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2087 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2090 GString* keycmd = g_string_new(uzbl.state.keycmd);
2091 g_string_append (keycmd, str);
2092 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2099 if (event->keyval == GDK_BackSpace)
2100 keycmd_bs(NULL, NULL, NULL);
2102 gboolean key_ret = FALSE;
2103 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2106 GString* keycmd = g_string_new(uzbl.state.keycmd);
2107 g_string_append(keycmd, event->string);
2108 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2111 run_keycmd(key_ret);
2113 if (key_ret) return (!uzbl.behave.insert_mode);
2118 run_keycmd(const gboolean key_ret) {
2119 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2121 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2123 parse_command(act->name, act->param, NULL);
2127 /* try if it's an incremental keycmd or one that takes args, and run it */
2128 GString* short_keys = g_string_new ("");
2129 GString* short_keys_inc = g_string_new ("");
2131 guint len = strlen(uzbl.state.keycmd);
2132 for (i=0; i<len; i++) {
2133 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2134 g_string_assign(short_keys_inc, short_keys->str);
2135 g_string_append_c(short_keys, '_');
2136 g_string_append_c(short_keys_inc, '*');
2138 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2139 /* run normal cmds only if return was pressed */
2140 exec_paramcmd(act, i);
2143 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2144 if (key_ret) /* just quit the incremental command on return */
2146 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2150 g_string_truncate(short_keys, short_keys->len - 1);
2152 g_string_free (short_keys, TRUE);
2153 g_string_free (short_keys_inc, TRUE);
2157 exec_paramcmd(const Action *act, const guint i) {
2158 GString *parampart = g_string_new (uzbl.state.keycmd);
2159 GString *actionname = g_string_new ("");
2160 GString *actionparam = g_string_new ("");
2161 g_string_erase (parampart, 0, i+1);
2163 g_string_printf (actionname, act->name, parampart->str);
2165 g_string_printf (actionparam, act->param, parampart->str);
2166 parse_command(actionname->str, actionparam->str, NULL);
2167 g_string_free(actionname, TRUE);
2168 g_string_free(actionparam, TRUE);
2169 g_string_free(parampart, TRUE);
2177 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2178 //main_window_ref = g_object_ref(scrolled_window);
2179 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
2181 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2182 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2184 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2185 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2189 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2190 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2191 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2192 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2193 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2194 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2196 return scrolled_window;
2203 g->mainbar = gtk_hbox_new (FALSE, 0);
2205 /* keep a reference to the bar so we can re-pack it at runtime*/
2206 //sbar_ref = g_object_ref(g->mainbar);
2208 g->mainbar_label = gtk_label_new ("");
2209 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2210 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2211 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2212 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2213 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2214 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2219 GtkWidget* create_window () {
2220 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2221 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2222 gtk_widget_set_name (window, "Uzbl browser");
2223 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2224 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2225 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2231 GtkPlug* create_plug () {
2232 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2233 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2234 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2241 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2243 If actname is one that calls an external command, this function will inject
2244 newargs in front of the user-provided args in that command line. They will
2245 come become after the body of the script (in sh) or after the name of
2246 the command to execute (in spawn).
2247 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2248 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2250 The return value consist of two strings: the action (sh, ...) and its args.
2252 If act is not one that calls an external command, then the given action merely
2255 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2256 gchar *actdup = g_strdup(actname);
2257 g_array_append_val(rets, actdup);
2259 if ((g_strcmp0(actname, "spawn") == 0) ||
2260 (g_strcmp0(actname, "sh") == 0) ||
2261 (g_strcmp0(actname, "sync_spawn") == 0) ||
2262 (g_strcmp0(actname, "sync_sh") == 0)) {
2264 GString *a = g_string_new("");
2265 gchar **spawnparts = split_quoted(origargs, FALSE);
2266 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2267 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2269 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2270 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2272 g_array_append_val(rets, a->str);
2273 g_string_free(a, FALSE);
2274 g_strfreev(spawnparts);
2276 gchar *origdup = g_strdup(origargs);
2277 g_array_append_val(rets, origdup);
2279 return (gchar**)g_array_free(rets, FALSE);
2283 run_handler (const gchar *act, const gchar *args) {
2284 /* Consider this code a temporary hack to make the handlers usable.
2285 In practice, all this splicing, injection, and reconstruction is
2286 inefficient, annoying and hard to manage. Potential pitfalls arise
2287 when the handler specific args 1) are not quoted (the handler
2288 callbacks should take care of this) 2) are quoted but interfere
2289 with the users' own quotation. A more ideal solution is
2290 to refactor parse_command so that it doesn't just take a string
2291 and execute it; rather than that, we should have a function which
2292 returns the argument vector parsed from the string. This vector
2293 could be modified (e.g. insert additional args into it) before
2294 passing it to the next function that actually executes it. Though
2295 it still isn't perfect for chain actions.. will reconsider & re-
2296 factor when I have the time. -duc */
2298 char **parts = g_strsplit(act, " ", 2);
2300 if (g_strcmp0(parts[0], "chain") == 0) {
2301 GString *newargs = g_string_new("");
2302 gchar **chainparts = split_quoted(parts[1], FALSE);
2304 /* for every argument in the chain, inject the handler args
2305 and make sure the new parts are wrapped in quotes */
2306 gchar **cp = chainparts;
2308 gchar *quotless = NULL;
2309 gchar **spliced_quotless = NULL; // sigh -_-;
2310 gchar **inpart = NULL;
2313 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2315 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2316 } else quotless = g_strdup(*cp);
2318 spliced_quotless = g_strsplit(quotless, " ", 2);
2319 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2320 g_strfreev(spliced_quotless);
2322 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2328 parse_command(parts[0], &(newargs->str[1]), NULL);
2329 g_string_free(newargs, TRUE);
2330 g_strfreev(chainparts);
2333 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2334 parse_command(inparts[0], inparts[1], NULL);
2342 add_binding (const gchar *key, const gchar *act) {
2343 char **parts = g_strsplit(act, " ", 2);
2350 if (uzbl.state.verbose)
2351 printf ("Binding %-10s : %s\n", key, act);
2352 action = new_action(parts[0], parts[1]);
2354 if (g_hash_table_remove (uzbl.bindings, key))
2355 g_warning ("Overwriting existing binding for \"%s\"", key);
2356 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2361 get_xdg_var (XDG_Var xdg) {
2362 const gchar* actual_value = getenv (xdg.environmental);
2363 const gchar* home = getenv ("HOME");
2364 gchar* return_value;
2366 if (! actual_value || strcmp (actual_value, "") == 0) {
2367 if (xdg.default_value) {
2368 return_value = str_replace ("~", home, xdg.default_value);
2370 return_value = NULL;
2373 return_value = str_replace("~", home, actual_value);
2376 return return_value;
2380 find_xdg_file (int xdg_type, char* filename) {
2381 /* xdg_type = 0 => config
2382 xdg_type = 1 => data
2383 xdg_type = 2 => cache*/
2385 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2386 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2389 gchar* temporary_string;
2393 if (! file_exists (temporary_file) && xdg_type != 2) {
2394 buf = get_xdg_var (XDG[3 + xdg_type]);
2395 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2398 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2399 g_free (temporary_file);
2400 temporary_file = g_strconcat (temporary_string, filename, NULL);
2404 //g_free (temporary_string); - segfaults.
2406 if (file_exists (temporary_file)) {
2407 return temporary_file;
2414 State *s = &uzbl.state;
2415 Network *n = &uzbl.net;
2417 for (i = 0; default_config[i].command != NULL; i++) {
2418 parse_cmd_line(default_config[i].command, NULL);
2421 if (g_strcmp0(s->config_file, "-") == 0) {
2422 s->config_file = NULL;
2426 else if (!s->config_file) {
2427 s->config_file = find_xdg_file (0, "/uzbl/config");
2430 if (s->config_file) {
2431 GArray* lines = read_file_by_line (s->config_file);
2435 while ((line = g_array_index(lines, gchar*, i))) {
2436 parse_cmd_line (line, NULL);
2440 g_array_free (lines, TRUE);
2442 if (uzbl.state.verbose)
2443 printf ("No configuration file loaded.\n");
2446 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2449 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2452 if (!uzbl.behave.cookie_handler)
2455 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2456 GString *s = g_string_new ("");
2457 SoupURI * soup_uri = soup_message_get_uri(msg);
2458 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2459 run_handler(uzbl.behave.cookie_handler, s->str);
2461 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2462 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2463 if ( p != NULL ) *p = '\0';
2464 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2466 if (uzbl.comm.sync_stdout)
2467 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2469 g_string_free(s, TRUE);
2473 save_cookies (SoupMessage *msg, gpointer user_data){
2477 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2478 cookie = soup_cookie_to_set_cookie_header(ck->data);
2479 SoupURI * soup_uri = soup_message_get_uri(msg);
2480 GString *s = g_string_new ("");
2481 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2482 run_handler(uzbl.behave.cookie_handler, s->str);
2484 g_string_free(s, TRUE);
2489 /* --- WEBINSPECTOR --- */
2491 hide_window_cb(GtkWidget *widget, gpointer data) {
2494 gtk_widget_hide(widget);
2497 static WebKitWebView*
2498 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2501 (void) web_inspector;
2502 GtkWidget* scrolled_window;
2503 GtkWidget* new_web_view;
2506 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2507 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2508 G_CALLBACK(hide_window_cb), NULL);
2510 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2511 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2512 gtk_widget_show(g->inspector_window);
2514 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2515 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2516 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2517 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2518 gtk_widget_show(scrolled_window);
2520 new_web_view = webkit_web_view_new();
2521 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2523 return WEBKIT_WEB_VIEW(new_web_view);
2527 inspector_show_window_cb (WebKitWebInspector* inspector){
2529 gtk_widget_show(uzbl.gui.inspector_window);
2533 /* TODO: Add variables and code to make use of these functions */
2535 inspector_close_window_cb (WebKitWebInspector* inspector){
2541 inspector_attach_window_cb (WebKitWebInspector* inspector){
2547 inspector_detach_window_cb (WebKitWebInspector* inspector){
2553 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2559 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2565 set_up_inspector() {
2567 WebKitWebSettings *settings = view_settings();
2568 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2570 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2571 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2573 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2574 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2575 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2576 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2578 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2582 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2584 uzbl_cmdprop *c = v;
2589 if(c->type == TYPE_STR)
2590 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2591 else if(c->type == TYPE_INT)
2592 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2596 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2600 printf("bind %s = %s %s\n", (char *)k ,
2601 (char *)a->name, a->param?(char *)a->param:"");
2605 dump_config() { //ADD "result" var so we can use this with uzblctrl
2606 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2607 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2611 retrieve_geometry() {
2613 GString *buf = g_string_new("");
2615 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2616 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2618 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2620 if(uzbl.gui.geometry)
2621 g_free(uzbl.gui.geometry);
2622 uzbl.gui.geometry = g_string_free(buf, FALSE);
2625 /* set up gtk, gobject, variable defaults and other things that tests and other
2626 * external applications need to do anyhow */
2628 initialize(int argc, char *argv[]) {
2629 gtk_init (&argc, &argv);
2630 if (!g_thread_supported ())
2631 g_thread_init (NULL);
2632 uzbl.state.executable_path = g_strdup(argv[0]);
2633 uzbl.state.selected_url = NULL;
2634 uzbl.state.searchtx = NULL;
2636 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2637 g_option_context_add_main_entries (context, entries, NULL);
2638 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2639 g_option_context_parse (context, &argc, &argv, NULL);
2640 g_option_context_free(context);
2642 if (uzbl.behave.print_version) {
2643 printf("Commit: %s\n", COMMIT);
2647 /* initialize hash table */
2648 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2650 uzbl.net.soup_session = webkit_get_default_session();
2651 uzbl.state.keycmd = g_strdup("");
2653 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2654 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2655 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2656 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2657 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2658 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2660 uzbl.gui.sbar.progress_s = g_strdup("=");
2661 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2662 uzbl.gui.sbar.progress_w = 10;
2664 /* HTML mode defaults*/
2665 uzbl.behave.html_buffer = g_string_new("");
2666 uzbl.behave.html_endmarker = g_strdup(".");
2667 uzbl.behave.html_timeout = 60;
2668 uzbl.behave.base_url = g_strdup("http://invalid");
2670 /* default mode indicators */
2671 uzbl.behave.insert_indicator = g_strdup("I");
2672 uzbl.behave.cmd_indicator = g_strdup("C");
2674 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2675 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2676 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2677 uzbl.info.arch = ARCH;
2678 uzbl.info.commit = COMMIT;
2681 make_var_to_name_hash();
2683 uzbl.gui.scrolled_win = create_browser();
2686 #ifndef UZBL_LIBRARY
2689 main (int argc, char* argv[]) {
2690 initialize(argc, argv);
2692 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2696 /* initial packing */
2697 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2698 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2700 if (uzbl.state.socket_id) {
2701 uzbl.gui.plug = create_plug ();
2702 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2703 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2705 uzbl.gui.main_window = create_window ();
2706 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2707 gtk_widget_show_all (uzbl.gui.main_window);
2708 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2711 if(!uzbl.state.instance_name)
2712 uzbl.state.instance_name = itos((int)uzbl.xwin);
2714 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2716 if (uzbl.state.verbose) {
2717 printf("Uzbl start location: %s\n", argv[0]);
2718 if (uzbl.state.socket_id)
2719 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2721 printf("window_id %i\n",(int) uzbl.xwin);
2722 printf("pid %i\n", getpid ());
2723 printf("name: %s\n", uzbl.state.instance_name);
2726 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2727 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2728 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2729 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2730 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2732 if(uzbl.gui.geometry)
2735 retrieve_geometry();
2737 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2738 gboolean verbose_override = uzbl.state.verbose;
2741 set_insert_mode(FALSE);
2743 if (!uzbl.behave.show_status)
2744 gtk_widget_hide(uzbl.gui.mainbar);
2751 if (verbose_override > uzbl.state.verbose)
2752 uzbl.state.verbose = verbose_override;
2755 set_var_value("uri", uri_override);
2756 g_free(uri_override);
2757 } else if (uzbl.state.uri)
2758 cmd_load_uri(uzbl.gui.web_view, NULL);
2763 return EXIT_SUCCESS;
2767 /* vi: set et ts=4: */