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])
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
45 #include <webkit/webkit.h>
46 #include <libsoup/soup.h>
47 #include <JavaScriptCore/JavaScript.h>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
77 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
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 enum ptr_type {TYPE_INT, TYPE_STR, TYPE_FLOAT};
85 /* associate command names to their properties */
86 typedef const struct {
87 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
88 the PTR() macro is kind of preventing this change at the moment. */
93 /*@null@*/ void (*func)(void);
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_indicator)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
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;
183 /*@null@*/ char *key;
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 array for quick access */
208 make_var_to_name_hash() {
209 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
211 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
216 /* --- UTILITY FUNCTIONS --- */
217 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
219 get_exp_type(const gchar *s) {
223 else if(*(s+1) == '{')
224 return EXP_BRACED_VAR;
225 else if(*(s+1) == '<')
227 else if(*(s+1) == '[')
230 return EXP_SIMPLE_VAR;
237 * recurse == 1: don't expand '@(command)@'
238 * recurse == 2: don't expand '@<java script>@'
241 expand(const char *s, guint recurse) {
244 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, '}');
272 if(!vend) vend = strchr(s, '\0');
276 vend = strstr(s, ")@");
277 if(!vend) vend = strchr(s, '\0');
281 vend = strstr(s, ">@");
282 if(!vend) vend = strchr(s, '\0');
286 vend = strstr(s, "]@");
287 if(!vend) vend = strchr(s, '\0');
292 ret = strndup(s, vend-s);
296 if(etype == EXP_SIMPLE_VAR ||
297 etype == EXP_BRACED_VAR) {
298 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
299 if(c->type == TYPE_STR && *c->ptr != NULL) {
300 g_string_append(buf, (gchar *)*c->ptr);
301 } else if(c->type == TYPE_INT) {
302 g_string_append_printf(buf, "%d", (int)*c->ptr);
304 else if(c->type == TYPE_FLOAT) {
305 g_string_append_printf(buf, "%f", *(float *)c->ptr);
309 if(etype == EXP_SIMPLE_VAR)
314 else if(recurse != 1 &&
316 mycmd = expand(ret, 1);
317 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
321 g_printerr("error on running command: %s\n", err->message);
324 else if (*cmd_stdout) {
325 size_t len = strlen(cmd_stdout);
327 if(len > 0 && cmd_stdout[len-1] == '\n')
328 cmd_stdout[--len] = '\0'; /* strip trailing newline */
330 g_string_append(buf, cmd_stdout);
335 else if(recurse != 2 &&
337 mycmd = expand(ret, 2);
338 eval_js(uzbl.gui.web_view, mycmd, js_ret);
342 g_string_append(buf, js_ret->str);
343 g_string_free(js_ret, TRUE);
344 js_ret = g_string_new("");
348 else if(etype == EXP_ESCAPE) {
349 mycmd = expand(ret, 0);
350 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
352 g_string_append(buf, escaped);
364 g_string_append_c(buf, *s);
369 g_string_free(js_ret, TRUE);
370 return g_string_free(buf, FALSE);
377 snprintf(tmp, sizeof(tmp), "%i", val);
378 return g_strdup(tmp);
382 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
385 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
388 str_replace (const char* search, const char* replace, const char* string) {
392 buf = g_strsplit (string, search, -1);
393 ret = g_strjoinv (replace, buf);
394 g_strfreev(buf); // somebody said this segfaults
400 read_file_by_line (gchar *path) {
401 GIOChannel *chan = NULL;
402 gchar *readbuf = NULL;
404 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
407 chan = g_io_channel_new_file(path, "r", NULL);
410 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
411 const gchar* val = g_strdup (readbuf);
412 g_array_append_val (lines, val);
417 g_io_channel_unref (chan);
419 fprintf(stderr, "File '%s' not be read.\n", path);
426 parseenv (char* string) {
427 extern char** environ;
428 gchar* tmpstr = NULL;
432 while (environ[i] != NULL) {
433 gchar** env = g_strsplit (environ[i], "=", 2);
434 gchar* envname = g_strconcat ("$", env[0], NULL);
436 if (g_strrstr (string, envname) != NULL) {
437 tmpstr = g_strdup(string);
439 string = str_replace(envname, env[1], tmpstr);
444 g_strfreev (env); // somebody said this breaks uzbl
452 setup_signal(int signr, sigfunc *shandler) {
453 struct sigaction nh, oh;
455 nh.sa_handler = shandler;
456 sigemptyset(&nh.sa_mask);
459 if(sigaction(signr, &nh, &oh) < 0)
467 if (uzbl.behave.fifo_dir)
468 unlink (uzbl.comm.fifo_path);
469 if (uzbl.behave.socket_dir)
470 unlink (uzbl.comm.socket_path);
472 g_free(uzbl.state.executable_path);
473 g_free(uzbl.state.keycmd);
474 g_hash_table_destroy(uzbl.bindings);
475 g_hash_table_destroy(uzbl.behave.commands);
478 /* used for html_mode_timeout
479 * be sure to extend this function to use
480 * more timers if needed in other places
483 set_timeout(int seconds) {
485 memset(&t, 0, sizeof t);
487 t.it_value.tv_sec = seconds;
488 t.it_value.tv_usec = 0;
489 setitimer(ITIMER_REAL, &t, NULL);
492 /* --- SIGNAL HANDLER --- */
495 catch_sigterm(int s) {
501 catch_sigint(int s) {
511 set_var_value("mode", "0");
516 /* --- CALLBACKS --- */
519 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
522 (void) navigation_action;
523 (void) policy_decision;
525 const gchar* uri = webkit_network_request_get_uri (request);
526 if (uzbl.state.verbose)
527 printf("New window requested -> %s \n", uri);
528 webkit_web_policy_decision_use(policy_decision);
533 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
538 /* If we can display it, let's display it... */
539 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
540 webkit_web_policy_decision_use (policy_decision);
544 /* ...everything we can't displayed is downloaded */
545 webkit_web_policy_decision_download (policy_decision);
550 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
554 if (uzbl.state.selected_url != NULL) {
555 if (uzbl.state.verbose)
556 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
557 new_window_load_uri(uzbl.state.selected_url);
559 if (uzbl.state.verbose)
560 printf("New web view -> %s\n","Nothing to open, exiting");
566 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
569 if (uzbl.behave.download_handler) {
570 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
571 if (uzbl.state.verbose)
572 printf("Download -> %s\n",uri);
573 /* if urls not escaped, we may have to escape and quote uri before this call */
574 run_handler(uzbl.behave.download_handler, uri);
579 /* scroll a bar in a given direction */
581 scroll (GtkAdjustment* bar, GArray *argv) {
585 gdouble page_size = gtk_adjustment_get_page_size(bar);
586 gdouble value = gtk_adjustment_get_value(bar);
587 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
590 value += page_size * amount * 0.01;
594 max_value = gtk_adjustment_get_upper(bar) - page_size;
596 if (value > max_value)
597 value = max_value; /* don't scroll past the end of the page */
599 gtk_adjustment_set_value (bar, value);
603 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
604 (void) page; (void) argv; (void) result;
605 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
609 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
610 (void) page; (void) argv; (void) result;
611 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
612 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
616 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) result;
618 scroll(uzbl.gui.bar_v, argv);
622 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
623 (void) page; (void) result;
624 scroll(uzbl.gui.bar_h, argv);
629 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
630 if(uzbl.state.verbose)
631 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
633 /* update geometry var with the actual geometry
634 this is necessary as some WMs don't seem to honour
635 the above setting and we don't want to end up with
636 wrong geometry information
643 if (!uzbl.behave.show_status) {
644 gtk_widget_hide(uzbl.gui.mainbar);
646 gtk_widget_show(uzbl.gui.mainbar);
652 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
657 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
661 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
666 if (uzbl.behave.show_status) {
667 gtk_widget_hide(uzbl.gui.mainbar);
669 gtk_widget_show(uzbl.gui.mainbar);
671 uzbl.behave.show_status = !uzbl.behave.show_status;
676 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
680 //Set selected_url state variable
681 g_free(uzbl.state.selected_url);
682 uzbl.state.selected_url = NULL;
684 uzbl.state.selected_url = g_strdup(link);
690 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
693 const gchar *title = webkit_web_view_get_title(web_view);
694 if (uzbl.gui.main_title)
695 g_free (uzbl.gui.main_title);
696 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
701 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
704 uzbl.gui.sbar.load_progress = progress;
706 g_free(uzbl.gui.sbar.progress_bar);
707 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
713 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
717 if (uzbl.behave.load_finish_handler)
718 run_handler(uzbl.behave.load_finish_handler, "");
721 void clear_keycmd() {
722 g_free(uzbl.state.keycmd);
723 uzbl.state.keycmd = g_strdup("");
727 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
731 uzbl.gui.sbar.load_progress = 0;
732 clear_keycmd(); // don't need old commands to remain on new page?
733 if (uzbl.behave.load_start_handler)
734 run_handler(uzbl.behave.load_start_handler, "");
738 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
741 g_free (uzbl.state.uri);
742 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
743 uzbl.state.uri = g_string_free (newuri, FALSE);
744 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
745 set_insert_mode(uzbl.behave.always_insert_mode);
748 if (uzbl.behave.load_commit_handler)
749 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
753 destroy_cb (GtkWidget* widget, gpointer data) {
761 if (uzbl.behave.history_handler) {
763 struct tm * timeinfo;
766 timeinfo = localtime ( &rawtime );
767 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
768 run_handler(uzbl.behave.history_handler, date);
773 /* VIEW funcs (little webkit wrappers) */
774 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
776 VIEWFUNC(reload_bypass_cache)
777 VIEWFUNC(stop_loading)
784 /* -- command to callback/function map for things we cannot attach to any signals */
785 struct {char *key; CommandInfo value;} cmdlist[] =
786 { /* key function no_split */
787 { "back", {view_go_back, 0} },
788 { "forward", {view_go_forward, 0} },
789 { "scroll_vert", {scroll_vert, 0} },
790 { "scroll_horz", {scroll_horz, 0} },
791 { "scroll_begin", {scroll_begin, 0} },
792 { "scroll_end", {scroll_end, 0} },
793 { "reload", {view_reload, 0}, },
794 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
795 { "stop", {view_stop_loading, 0}, },
796 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
797 { "zoom_out", {view_zoom_out, 0}, },
798 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
799 { "uri", {load_uri, TRUE} },
800 { "js", {run_js, TRUE} },
801 { "script", {run_external_js, 0} },
802 { "toggle_status", {toggle_status_cb, 0} },
803 { "spawn", {spawn, 0} },
804 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
805 { "sh", {spawn_sh, 0} },
806 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
807 { "exit", {close_uzbl, 0} },
808 { "search", {search_forward_text, TRUE} },
809 { "search_reverse", {search_reverse_text, TRUE} },
810 { "dehilight", {dehilight, 0} },
811 { "toggle_insert_mode", {toggle_insert_mode, 0} },
812 { "set", {set_var, TRUE} },
813 //{ "get", {get_var, TRUE} },
814 { "bind", {act_bind, TRUE} },
815 { "dump_config", {act_dump_config, 0} },
816 { "keycmd", {keycmd, TRUE} },
817 { "keycmd_nl", {keycmd_nl, TRUE} },
818 { "keycmd_bs", {keycmd_bs, 0} },
819 { "chain", {chain, 0} },
820 { "print", {print, TRUE} }
827 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
829 for (i = 0; i < LENGTH(cmdlist); i++)
830 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
833 /* -- CORE FUNCTIONS -- */
836 free_action(gpointer act) {
837 Action *action = (Action*)act;
838 g_free(action->name);
840 g_free(action->param);
845 new_action(const gchar *name, const gchar *param) {
846 Action *action = g_new(Action, 1);
848 action->name = g_strdup(name);
850 action->param = g_strdup(param);
852 action->param = NULL;
858 file_exists (const char * filename) {
859 return (access(filename, F_OK) == 0);
863 set_var(WebKitWebView *page, GArray *argv, GString *result) {
864 (void) page; (void) result;
865 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
866 if (split[0] != NULL) {
867 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
868 set_var_value(g_strstrip(split[0]), value);
875 print(WebKitWebView *page, GArray *argv, GString *result) {
876 (void) page; (void) result;
879 buf = expand(argv_idx(argv, 0), 0);
880 g_string_assign(result, buf);
885 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
886 (void) page; (void) result;
887 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
888 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
889 add_binding(g_strstrip(split[0]), value);
907 set_mode_indicator() {
908 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
909 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
914 set_mode_indicator();
919 set_insert_mode(gboolean mode) {
920 uzbl.behave.insert_mode = mode;
921 set_mode_indicator();
925 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
926 (void) page; (void) result;
928 if (argv_idx(argv, 0)) {
929 if (strcmp (argv_idx(argv, 0), "0") == 0) {
930 set_insert_mode(FALSE);
932 set_insert_mode(TRUE);
935 set_insert_mode( !uzbl.behave.insert_mode );
942 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
945 if (argv_idx(argv, 0)) {
946 GString* newuri = g_string_new (argv_idx(argv, 0));
947 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
948 run_js(web_view, argv, NULL);
951 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
952 g_string_prepend (newuri, "http://");
953 /* if we do handle cookies, ask our handler for them */
954 webkit_web_view_load_uri (web_view, newuri->str);
955 g_string_free (newuri, TRUE);
962 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
963 size_t argumentCount, const JSValueRef arguments[],
964 JSValueRef* exception) {
969 JSStringRef js_result_string;
970 GString *result = g_string_new("");
972 if (argumentCount >= 1) {
973 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
974 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
975 char ctl_line[arg_size];
976 JSStringGetUTF8CString(arg, ctl_line, arg_size);
978 parse_cmd_line(ctl_line, result);
980 JSStringRelease(arg);
982 js_result_string = JSStringCreateWithUTF8CString(result->str);
984 g_string_free(result, TRUE);
986 return JSValueMakeString(ctx, js_result_string);
989 JSStaticFunction js_static_functions[] = {
990 {"run", js_run_command, kJSPropertyAttributeNone},
995 /* This function creates the class and its definition, only once */
996 if (!uzbl.js.initialized) {
997 /* it would be pretty cool to make this dynamic */
998 uzbl.js.classdef = kJSClassDefinitionEmpty;
999 uzbl.js.classdef.staticFunctions = js_static_functions;
1001 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1007 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1008 WebKitWebFrame *frame;
1009 JSGlobalContextRef context;
1010 JSObjectRef globalobject;
1011 JSStringRef var_name;
1013 JSStringRef js_script;
1014 JSValueRef js_result;
1015 JSStringRef js_result_string;
1016 size_t js_result_size;
1020 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1021 context = webkit_web_frame_get_global_context(frame);
1022 globalobject = JSContextGetGlobalObject(context);
1024 /* uzbl javascript namespace */
1025 var_name = JSStringCreateWithUTF8CString("Uzbl");
1026 JSObjectSetProperty(context, globalobject, var_name,
1027 JSObjectMake(context, uzbl.js.classref, NULL),
1028 kJSClassAttributeNone, NULL);
1030 /* evaluate the script and get return value*/
1031 js_script = JSStringCreateWithUTF8CString(script);
1032 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1033 if (js_result && !JSValueIsUndefined(context, js_result)) {
1034 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1035 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1037 if (js_result_size) {
1038 char js_result_utf8[js_result_size];
1039 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1040 g_string_assign(result, js_result_utf8);
1043 JSStringRelease(js_result_string);
1047 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1049 JSStringRelease(var_name);
1050 JSStringRelease(js_script);
1054 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1055 if (argv_idx(argv, 0))
1056 eval_js(web_view, argv_idx(argv, 0), result);
1060 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1062 if (argv_idx(argv, 0)) {
1063 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1068 while ((line = g_array_index(lines, gchar*, i))) {
1070 js = g_strdup (line);
1072 gchar* newjs = g_strconcat (js, line, NULL);
1079 if (uzbl.state.verbose)
1080 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1082 if (argv_idx (argv, 1)) {
1083 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1087 eval_js (web_view, js, result);
1089 g_array_free (lines, TRUE);
1094 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1095 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1096 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1097 webkit_web_view_unmark_text_matches (page);
1098 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1099 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1103 if (uzbl.state.searchtx) {
1104 if (uzbl.state.verbose)
1105 printf ("Searching: %s\n", uzbl.state.searchtx);
1106 webkit_web_view_set_highlight_text_matches (page, TRUE);
1107 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1112 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1114 search_text(page, argv, TRUE);
1118 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1120 search_text(page, argv, FALSE);
1124 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1125 (void) argv; (void) result;
1126 webkit_web_view_set_highlight_text_matches (page, FALSE);
1131 new_window_load_uri (const gchar * uri) {
1132 if (uzbl.behave.new_window) {
1133 GString *s = g_string_new ("");
1134 g_string_printf(s, "'%s'", uri);
1135 run_handler(uzbl.behave.new_window, s->str);
1138 GString* to_execute = g_string_new ("");
1139 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1141 for (i = 0; entries[i].long_name != NULL; i++) {
1142 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1143 gchar** str = (gchar**)entries[i].arg_data;
1145 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1149 if (uzbl.state.verbose)
1150 printf("\n%s\n", to_execute->str);
1151 g_spawn_command_line_async (to_execute->str, NULL);
1152 g_string_free (to_execute, TRUE);
1156 chain (WebKitWebView *page, GArray *argv, GString *result) {
1157 (void) page; (void) result;
1159 gchar **parts = NULL;
1161 while ((a = argv_idx(argv, i++))) {
1162 parts = g_strsplit (a, " ", 2);
1164 parse_command(parts[0], parts[1], result);
1170 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1174 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1180 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1184 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1190 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1195 int len = strlen(uzbl.state.keycmd);
1196 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1198 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1203 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1210 /* --Statusbar functions-- */
1212 build_progressbar_ascii(int percent) {
1213 int width=uzbl.gui.sbar.progress_w;
1216 GString *bar = g_string_new("");
1218 l = (double)percent*((double)width/100.);
1219 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1221 for(i=0; i<(int)l; i++)
1222 g_string_append(bar, uzbl.gui.sbar.progress_s);
1225 g_string_append(bar, uzbl.gui.sbar.progress_u);
1227 return g_string_free(bar, FALSE);
1229 /* --End Statusbar functions-- */
1232 sharg_append(GArray *a, const gchar *str) {
1233 const gchar *s = (str ? str : "");
1234 g_array_append_val(a, s);
1237 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1239 run_command (const gchar *command, const guint npre, const gchar **args,
1240 const gboolean sync, char **output_stdout) {
1241 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1244 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1245 gchar *pid = itos(getpid());
1246 gchar *xwin = itos(uzbl.xwin);
1248 sharg_append(a, command);
1249 for (i = 0; i < npre; i++) /* add n args before the default vars */
1250 sharg_append(a, args[i]);
1251 sharg_append(a, uzbl.state.config_file);
1252 sharg_append(a, pid);
1253 sharg_append(a, xwin);
1254 sharg_append(a, uzbl.comm.fifo_path);
1255 sharg_append(a, uzbl.comm.socket_path);
1256 sharg_append(a, uzbl.state.uri);
1257 sharg_append(a, uzbl.gui.main_title);
1259 for (i = npre; i < g_strv_length((gchar**)args); i++)
1260 sharg_append(a, args[i]);
1264 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1266 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1267 NULL, NULL, output_stdout, NULL, NULL, &err);
1268 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1269 NULL, NULL, NULL, &err);
1271 if (uzbl.state.verbose) {
1272 GString *s = g_string_new("spawned:");
1273 for (i = 0; i < (a->len); i++) {
1274 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1275 g_string_append_printf(s, " %s", qarg);
1278 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1279 printf("%s\n", s->str);
1280 g_string_free(s, TRUE);
1282 printf("Stdout: %s\n", *output_stdout);
1286 g_printerr("error on run_command: %s\n", err->message);
1291 g_array_free (a, TRUE);
1296 split_quoted(const gchar* src, const gboolean unquote) {
1297 /* split on unquoted space, return array of strings;
1298 remove a layer of quotes and backslashes if unquote */
1299 if (!src) return NULL;
1301 gboolean dq = FALSE;
1302 gboolean sq = FALSE;
1303 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1304 GString *s = g_string_new ("");
1308 for (p = src; *p != '\0'; p++) {
1309 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1310 else if (*p == '\\') { g_string_append_c(s, *p++);
1311 g_string_append_c(s, *p); }
1312 else if ((*p == '"') && unquote && !sq) dq = !dq;
1313 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1315 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1316 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1318 else if ((*p == ' ') && !dq && !sq) {
1319 dup = g_strdup(s->str);
1320 g_array_append_val(a, dup);
1321 g_string_truncate(s, 0);
1322 } else g_string_append_c(s, *p);
1324 dup = g_strdup(s->str);
1325 g_array_append_val(a, dup);
1326 ret = (gchar**)a->data;
1327 g_array_free (a, FALSE);
1328 g_string_free (s, TRUE);
1333 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1334 (void)web_view; (void)result;
1335 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1336 if (argv_idx(argv, 0))
1337 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1341 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1342 (void)web_view; (void)result;
1344 if (argv_idx(argv, 0))
1345 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1346 TRUE, &uzbl.comm.sync_stdout);
1350 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1351 (void)web_view; (void)result;
1352 if (!uzbl.behave.shell_cmd) {
1353 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1358 gchar *spacer = g_strdup("");
1359 g_array_insert_val(argv, 1, spacer);
1360 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1362 for (i = 1; i < g_strv_length(cmd); i++)
1363 g_array_prepend_val(argv, cmd[i]);
1365 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1371 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1372 (void)web_view; (void)result;
1373 if (!uzbl.behave.shell_cmd) {
1374 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1379 gchar *spacer = g_strdup("");
1380 g_array_insert_val(argv, 1, spacer);
1381 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1383 for (i = 1; i < g_strv_length(cmd); i++)
1384 g_array_prepend_val(argv, cmd[i]);
1386 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1387 TRUE, &uzbl.comm.sync_stdout);
1393 parse_command(const char *cmd, const char *param, GString *result) {
1396 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1398 gchar **par = split_quoted(param, TRUE);
1399 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1401 if (c->no_split) { /* don't split */
1402 sharg_append(a, param);
1404 for (i = 0; i < g_strv_length(par); i++)
1405 sharg_append(a, par[i]);
1408 if (result == NULL) {
1409 GString *result_print = g_string_new("");
1411 c->function(uzbl.gui.web_view, a, result_print);
1412 if (result_print->len)
1413 printf("%*s\n", result_print->len, result_print->str);
1415 g_string_free(result_print, TRUE);
1417 c->function(uzbl.gui.web_view, a, result);
1420 g_array_free (a, TRUE);
1423 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1430 if(*uzbl.net.proxy_url == ' '
1431 || uzbl.net.proxy_url == NULL) {
1432 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1433 (GType) SOUP_SESSION_PROXY_URI);
1436 suri = soup_uri_new(uzbl.net.proxy_url);
1437 g_object_set(G_OBJECT(uzbl.net.soup_session),
1438 SOUP_SESSION_PROXY_URI,
1440 soup_uri_free(suri);
1447 if(file_exists(uzbl.gui.icon)) {
1448 if (uzbl.gui.main_window)
1449 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1451 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1457 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1458 g_array_append_val (a, uzbl.state.uri);
1459 load_uri(uzbl.gui.web_view, a, NULL);
1460 g_array_free (a, TRUE);
1464 cmd_always_insert_mode() {
1465 set_insert_mode(uzbl.behave.always_insert_mode);
1471 g_object_set(G_OBJECT(uzbl.net.soup_session),
1472 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1476 cmd_max_conns_host() {
1477 g_object_set(G_OBJECT(uzbl.net.soup_session),
1478 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1483 soup_session_remove_feature
1484 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1485 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1486 /*g_free(uzbl.net.soup_logger);*/
1488 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1489 soup_session_add_feature(uzbl.net.soup_session,
1490 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1495 return webkit_web_view_get_settings(uzbl.gui.web_view);
1500 WebKitWebSettings *ws = view_settings();
1501 if (uzbl.behave.font_size > 0) {
1502 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1505 if (uzbl.behave.monospace_size > 0) {
1506 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1507 uzbl.behave.monospace_size, NULL);
1509 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1510 uzbl.behave.font_size, NULL);
1516 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1520 cmd_disable_plugins() {
1521 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1522 !uzbl.behave.disable_plugins, NULL);
1526 cmd_disable_scripts() {
1527 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1528 !uzbl.behave.disable_scripts, NULL);
1532 cmd_minimum_font_size() {
1533 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1534 uzbl.behave.minimum_font_size, NULL);
1537 cmd_autoload_img() {
1538 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1539 uzbl.behave.autoload_img, NULL);
1544 cmd_autoshrink_img() {
1545 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1546 uzbl.behave.autoshrink_img, NULL);
1551 cmd_enable_spellcheck() {
1552 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1553 uzbl.behave.enable_spellcheck, NULL);
1557 cmd_enable_private() {
1558 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1559 uzbl.behave.enable_private, NULL);
1564 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1565 uzbl.behave.print_bg, NULL);
1570 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1571 uzbl.behave.style_uri, NULL);
1575 cmd_resizable_txt() {
1576 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1577 uzbl.behave.resizable_txt, NULL);
1581 cmd_default_encoding() {
1582 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1583 uzbl.behave.default_encoding, NULL);
1587 cmd_enforce_96dpi() {
1588 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1589 uzbl.behave.enforce_96dpi, NULL);
1593 cmd_caret_browsing() {
1594 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1595 uzbl.behave.caret_browsing, NULL);
1599 cmd_cookie_handler() {
1600 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1601 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1602 if ((g_strcmp0(split[0], "sh") == 0) ||
1603 (g_strcmp0(split[0], "spawn") == 0)) {
1604 g_free (uzbl.behave.cookie_handler);
1605 uzbl.behave.cookie_handler =
1606 g_strdup_printf("sync_%s %s", split[0], split[1]);
1613 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1614 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1615 if ((g_strcmp0(split[0], "sh") == 0) ||
1616 (g_strcmp0(split[0], "spawn") == 0)) {
1617 g_free (uzbl.behave.new_window);
1618 uzbl.behave.new_window =
1619 g_strdup_printf("%s %s", split[0], split[1]);
1626 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1631 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1636 if(uzbl.behave.inject_html) {
1637 webkit_web_view_load_html_string (uzbl.gui.web_view,
1638 uzbl.behave.inject_html, NULL);
1647 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1648 uzbl.behave.modmask = 0;
1650 if(uzbl.behave.modkey)
1651 g_free(uzbl.behave.modkey);
1652 uzbl.behave.modkey = buf;
1654 for (i = 0; modkeys[i].key != NULL; i++) {
1655 if (g_strrstr(buf, modkeys[i].key))
1656 uzbl.behave.modmask |= modkeys[i].mask;
1662 if (*uzbl.net.useragent == ' ') {
1663 g_free (uzbl.net.useragent);
1664 uzbl.net.useragent = NULL;
1666 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1667 uzbl.net.useragent, NULL);
1673 gtk_widget_ref(uzbl.gui.scrolled_win);
1674 gtk_widget_ref(uzbl.gui.mainbar);
1675 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1676 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1678 if(uzbl.behave.status_top) {
1679 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1680 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1683 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.mainbar, FALSE, TRUE, 0);
1686 gtk_widget_unref(uzbl.gui.scrolled_win);
1687 gtk_widget_unref(uzbl.gui.mainbar);
1688 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1693 set_var_value(gchar *name, gchar *val) {
1694 uzbl_cmdprop *c = NULL;
1698 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1699 if(!c->writeable) return FALSE;
1701 /* check for the variable type */
1702 if (c->type == TYPE_STR) {
1703 buf = expand(val, 0);
1706 } else if(c->type == TYPE_INT) {
1707 int *ip = (int *)c->ptr;
1708 buf = expand(val, 0);
1709 *ip = (int)strtoul(buf, &endp, 10);
1711 } else if (c->type == TYPE_FLOAT) {
1712 float *fp = (float *)c->ptr;
1713 buf = expand(val, 0);
1714 *fp = strtod(buf, &endp);
1718 /* invoke a command specific function */
1719 if(c->func) c->func();
1726 Behaviour *b = &uzbl.behave;
1728 if(b->html_buffer->str) {
1729 webkit_web_view_load_html_string (uzbl.gui.web_view,
1730 b->html_buffer->str, b->base_url);
1731 g_string_free(b->html_buffer, TRUE);
1732 b->html_buffer = g_string_new("");
1736 enum {M_CMD, M_HTML};
1738 parse_cmd_line(const char *ctl_line, GString *result) {
1739 Behaviour *b = &uzbl.behave;
1742 if(b->mode == M_HTML) {
1743 len = strlen(b->html_endmarker);
1744 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1745 if(len == strlen(ctl_line)-1 &&
1746 !strncmp(b->html_endmarker, ctl_line, len)) {
1748 set_var_value("mode", "0");
1753 set_timeout(b->html_timeout);
1754 g_string_append(b->html_buffer, ctl_line);
1757 else if((ctl_line[0] == '#') /* Comments */
1758 || (ctl_line[0] == ' ')
1759 || (ctl_line[0] == '\n'))
1760 ; /* ignore these lines */
1761 else { /* parse a command */
1763 gchar **tokens = NULL;
1764 len = strlen(ctl_line);
1766 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1767 ctlstrip = g_strndup(ctl_line, len - 1);
1768 else ctlstrip = g_strdup(ctl_line);
1770 tokens = g_strsplit(ctlstrip, " ", 2);
1771 parse_command(tokens[0], tokens[1], result);
1778 build_stream_name(int type, const gchar* dir) {
1779 State *s = &uzbl.state;
1783 str = g_strdup_printf
1784 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1785 } else if (type == SOCKET) {
1786 str = g_strdup_printf
1787 ("%s/uzbl_socket_%s", dir, s->instance_name);
1793 control_fifo(GIOChannel *gio, GIOCondition condition) {
1794 if (uzbl.state.verbose)
1795 printf("triggered\n");
1800 if (condition & G_IO_HUP)
1801 g_error ("Fifo: Read end of pipe died!\n");
1804 g_error ("Fifo: GIOChannel broke\n");
1806 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1807 if (ret == G_IO_STATUS_ERROR) {
1808 g_error ("Fifo: Error reading: %s\n", err->message);
1812 parse_cmd_line(ctl_line, NULL);
1819 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1820 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1821 if (unlink(uzbl.comm.fifo_path) == -1)
1822 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1823 g_free(uzbl.comm.fifo_path);
1824 uzbl.comm.fifo_path = NULL;
1827 GIOChannel *chan = NULL;
1828 GError *error = NULL;
1829 gchar *path = build_stream_name(FIFO, dir);
1831 if (!file_exists(path)) {
1832 if (mkfifo (path, 0666) == 0) {
1833 // 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.
1834 chan = g_io_channel_new_file(path, "r+", &error);
1836 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1837 if (uzbl.state.verbose)
1838 printf ("init_fifo: created successfully as %s\n", path);
1839 uzbl.comm.fifo_path = path;
1841 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1842 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1843 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1844 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1846 /* if we got this far, there was an error; cleanup */
1847 if (error) g_error_free (error);
1854 control_stdin(GIOChannel *gio, GIOCondition condition) {
1856 gchar *ctl_line = NULL;
1859 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1860 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1863 parse_cmd_line(ctl_line, NULL);
1871 GIOChannel *chan = NULL;
1872 GError *error = NULL;
1874 chan = g_io_channel_unix_new(fileno(stdin));
1876 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1877 g_error ("Stdin: could not add watch\n");
1879 if (uzbl.state.verbose)
1880 printf ("Stdin: watch added successfully\n");
1883 g_error ("Stdin: Error while opening: %s\n", error->message);
1885 if (error) g_error_free (error);
1889 control_socket(GIOChannel *chan) {
1890 struct sockaddr_un remote;
1891 unsigned int t = sizeof(remote);
1893 GIOChannel *clientchan;
1895 clientsock = accept (g_io_channel_unix_get_fd(chan),
1896 (struct sockaddr *) &remote, &t);
1898 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1899 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1900 (GIOFunc) control_client_socket, clientchan);
1907 control_client_socket(GIOChannel *clientchan) {
1909 GString *result = g_string_new("");
1910 GError *error = NULL;
1914 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1915 if (ret == G_IO_STATUS_ERROR) {
1916 g_warning ("Error reading: %s\n", error->message);
1917 g_io_channel_shutdown(clientchan, TRUE, &error);
1919 } else if (ret == G_IO_STATUS_EOF) {
1920 /* shutdown and remove channel watch from main loop */
1921 g_io_channel_shutdown(clientchan, TRUE, &error);
1926 parse_cmd_line (ctl_line, result);
1927 g_string_append_c(result, '\n');
1928 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1930 if (ret == G_IO_STATUS_ERROR) {
1931 g_warning ("Error writing: %s", error->message);
1933 g_io_channel_flush(clientchan, &error);
1936 if (error) g_error_free (error);
1937 g_string_free(result, TRUE);
1943 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1944 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1945 if (unlink(uzbl.comm.socket_path) == -1)
1946 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1947 g_free(uzbl.comm.socket_path);
1948 uzbl.comm.socket_path = NULL;
1956 GIOChannel *chan = NULL;
1958 struct sockaddr_un local;
1959 gchar *path = build_stream_name(SOCKET, dir);
1961 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1963 local.sun_family = AF_UNIX;
1964 strcpy (local.sun_path, path);
1965 unlink (local.sun_path);
1967 len = strlen (local.sun_path) + sizeof (local.sun_family);
1968 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1969 if (uzbl.state.verbose)
1970 printf ("init_socket: opened in %s\n", path);
1973 if( (chan = g_io_channel_unix_new(sock)) ) {
1974 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1975 uzbl.comm.socket_path = path;
1978 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1980 /* if we got this far, there was an error; cleanup */
1987 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1988 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1990 // this function may be called very early when the templates are not set (yet), hence the checks
1992 update_title (void) {
1993 Behaviour *b = &uzbl.behave;
1996 if (b->show_status) {
1997 if (b->title_format_short) {
1998 parsed = expand(b->title_format_short, 0);
1999 if (uzbl.gui.main_window)
2000 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2003 if (b->status_format) {
2004 parsed = expand(b->status_format, 0);
2005 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2008 if (b->status_background) {
2010 gdk_color_parse (b->status_background, &color);
2011 //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)
2012 if (uzbl.gui.main_window)
2013 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2014 else if (uzbl.gui.plug)
2015 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2018 if (b->title_format_long) {
2019 parsed = expand(b->title_format_long, 0);
2020 if (uzbl.gui.main_window)
2021 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2028 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2032 retreive_geometry();
2037 key_press_cb (GtkWidget* window, GdkEventKey* event)
2039 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2043 if (event->type != GDK_KEY_PRESS ||
2044 event->keyval == GDK_Page_Up ||
2045 event->keyval == GDK_Page_Down ||
2046 event->keyval == GDK_Up ||
2047 event->keyval == GDK_Down ||
2048 event->keyval == GDK_Left ||
2049 event->keyval == GDK_Right ||
2050 event->keyval == GDK_Shift_L ||
2051 event->keyval == GDK_Shift_R)
2054 /* turn off insert mode (if always_insert_mode is not used) */
2055 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2056 set_insert_mode(uzbl.behave.always_insert_mode);
2061 if (uzbl.behave.insert_mode &&
2062 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2063 (!uzbl.behave.modmask)
2068 if (event->keyval == GDK_Escape) {
2071 dehilight(uzbl.gui.web_view, NULL, NULL);
2075 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2076 if (event->keyval == GDK_Insert) {
2078 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2079 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2081 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2084 GString* keycmd = g_string_new(uzbl.state.keycmd);
2085 g_string_append (keycmd, str);
2086 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2093 if (event->keyval == GDK_BackSpace)
2094 keycmd_bs(NULL, NULL, NULL);
2096 gboolean key_ret = FALSE;
2097 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2100 GString* keycmd = g_string_new(uzbl.state.keycmd);
2101 g_string_append(keycmd, event->string);
2102 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2105 run_keycmd(key_ret);
2107 if (key_ret) return (!uzbl.behave.insert_mode);
2112 run_keycmd(const gboolean key_ret) {
2113 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2115 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2117 parse_command(act->name, act->param, NULL);
2121 /* try if it's an incremental keycmd or one that takes args, and run it */
2122 GString* short_keys = g_string_new ("");
2123 GString* short_keys_inc = g_string_new ("");
2125 guint len = strlen(uzbl.state.keycmd);
2126 for (i=0; i<len; i++) {
2127 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2128 g_string_assign(short_keys_inc, short_keys->str);
2129 g_string_append_c(short_keys, '_');
2130 g_string_append_c(short_keys_inc, '*');
2132 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2133 /* run normal cmds only if return was pressed */
2134 exec_paramcmd(act, i);
2137 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2138 if (key_ret) /* just quit the incremental command on return */
2140 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2144 g_string_truncate(short_keys, short_keys->len - 1);
2146 g_string_free (short_keys, TRUE);
2147 g_string_free (short_keys_inc, TRUE);
2151 exec_paramcmd(const Action *act, const guint i) {
2152 GString *parampart = g_string_new (uzbl.state.keycmd);
2153 GString *actionname = g_string_new ("");
2154 GString *actionparam = g_string_new ("");
2155 g_string_erase (parampart, 0, i+1);
2157 g_string_printf (actionname, act->name, parampart->str);
2159 g_string_printf (actionparam, act->param, parampart->str);
2160 parse_command(actionname->str, actionparam->str, NULL);
2161 g_string_free(actionname, TRUE);
2162 g_string_free(actionparam, TRUE);
2163 g_string_free(parampart, TRUE);
2171 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2172 //main_window_ref = g_object_ref(scrolled_window);
2173 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
2175 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2176 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2178 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2179 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2180 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2181 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2182 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2183 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2184 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2190 return scrolled_window;
2197 g->mainbar = gtk_hbox_new (FALSE, 0);
2199 /* keep a reference to the bar so we can re-pack it at runtime*/
2200 //sbar_ref = g_object_ref(g->mainbar);
2202 g->mainbar_label = gtk_label_new ("");
2203 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2204 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2205 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2206 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2207 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2208 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2214 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2215 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2216 gtk_widget_set_name (window, "Uzbl browser");
2217 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2218 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2219 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2226 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2227 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2228 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2235 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2237 If actname is one that calls an external command, this function will inject
2238 newargs in front of the user-provided args in that command line. They will
2239 come become after the body of the script (in sh) or after the name of
2240 the command to execute (in spawn).
2241 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2242 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2244 The return value consist of two strings: the action (sh, ...) and its args.
2246 If act is not one that calls an external command, then the given action merely
2249 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2250 /* Arrr! Here be memory leaks */
2251 gchar *actdup = g_strdup(actname);
2252 g_array_append_val(rets, actdup);
2254 if ((g_strcmp0(actname, "spawn") == 0) ||
2255 (g_strcmp0(actname, "sh") == 0) ||
2256 (g_strcmp0(actname, "sync_spawn") == 0) ||
2257 (g_strcmp0(actname, "sync_sh") == 0)) {
2259 GString *a = g_string_new("");
2260 gchar **spawnparts = split_quoted(origargs, FALSE);
2261 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2262 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2264 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2265 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2267 g_array_append_val(rets, a->str);
2268 g_string_free(a, FALSE);
2269 g_strfreev(spawnparts);
2271 gchar *origdup = g_strdup(origargs);
2272 g_array_append_val(rets, origdup);
2274 return (gchar**)g_array_free(rets, FALSE);
2278 run_handler (const gchar *act, const gchar *args) {
2279 /* Consider this code a temporary hack to make the handlers usable.
2280 In practice, all this splicing, injection, and reconstruction is
2281 inefficient, annoying and hard to manage. Potential pitfalls arise
2282 when the handler specific args 1) are not quoted (the handler
2283 callbacks should take care of this) 2) are quoted but interfere
2284 with the users' own quotation. A more ideal solution is
2285 to refactor parse_command so that it doesn't just take a string
2286 and execute it; rather than that, we should have a function which
2287 returns the argument vector parsed from the string. This vector
2288 could be modified (e.g. insert additional args into it) before
2289 passing it to the next function that actually executes it. Though
2290 it still isn't perfect for chain actions.. will reconsider & re-
2291 factor when I have the time. -duc */
2293 char **parts = g_strsplit(act, " ", 2);
2295 if (g_strcmp0(parts[0], "chain") == 0) {
2296 GString *newargs = g_string_new("");
2297 gchar **chainparts = split_quoted(parts[1], FALSE);
2299 /* for every argument in the chain, inject the handler args
2300 and make sure the new parts are wrapped in quotes */
2301 gchar **cp = chainparts;
2303 gchar *quotless = NULL;
2304 gchar **spliced_quotless = NULL; // sigh -_-;
2305 gchar **inpart = NULL;
2308 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2310 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2311 } else quotless = g_strdup(*cp);
2313 spliced_quotless = g_strsplit(quotless, " ", 2);
2314 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2315 g_strfreev(spliced_quotless);
2317 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2323 parse_command(parts[0], &(newargs->str[1]), NULL);
2324 g_string_free(newargs, TRUE);
2325 g_strfreev(chainparts);
2328 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2329 parse_command(inparts[0], inparts[1], NULL);
2337 add_binding (const gchar *key, const gchar *act) {
2338 char **parts = g_strsplit(act, " ", 2);
2345 if (uzbl.state.verbose)
2346 printf ("Binding %-10s : %s\n", key, act);
2347 action = new_action(parts[0], parts[1]);
2349 if (g_hash_table_remove (uzbl.bindings, key))
2350 g_warning ("Overwriting existing binding for \"%s\"", key);
2351 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2356 get_xdg_var (XDG_Var xdg) {
2357 const gchar* actual_value = getenv (xdg.environmental);
2358 const gchar* home = getenv ("HOME");
2359 gchar* return_value;
2361 if (! actual_value || strcmp (actual_value, "") == 0) {
2362 if (xdg.default_value) {
2363 return_value = str_replace ("~", home, xdg.default_value);
2365 return_value = NULL;
2368 return_value = str_replace("~", home, actual_value);
2371 return return_value;
2375 find_xdg_file (int xdg_type, char* filename) {
2376 /* xdg_type = 0 => config
2377 xdg_type = 1 => data
2378 xdg_type = 2 => cache*/
2380 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2381 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2384 gchar* temporary_string;
2388 if (! file_exists (temporary_file) && xdg_type != 2) {
2389 buf = get_xdg_var (XDG[3 + xdg_type]);
2390 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2393 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2394 g_free (temporary_file);
2395 temporary_file = g_strconcat (temporary_string, filename, NULL);
2399 //g_free (temporary_string); - segfaults.
2401 if (file_exists (temporary_file)) {
2402 return temporary_file;
2409 State *s = &uzbl.state;
2410 Network *n = &uzbl.net;
2412 for (i = 0; default_config[i].command != NULL; i++) {
2413 parse_cmd_line(default_config[i].command, NULL);
2416 if (g_strcmp0(s->config_file, "-") == 0) {
2417 s->config_file = NULL;
2421 else if (!s->config_file) {
2422 s->config_file = find_xdg_file (0, "/uzbl/config");
2425 if (s->config_file) {
2426 GArray* lines = read_file_by_line (s->config_file);
2430 while ((line = g_array_index(lines, gchar*, i))) {
2431 parse_cmd_line (line, NULL);
2435 g_array_free (lines, TRUE);
2437 if (uzbl.state.verbose)
2438 printf ("No configuration file loaded.\n");
2441 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2444 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2447 if (!uzbl.behave.cookie_handler)
2450 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2451 GString *s = g_string_new ("");
2452 SoupURI * soup_uri = soup_message_get_uri(msg);
2453 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2454 run_handler(uzbl.behave.cookie_handler, s->str);
2456 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2457 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2458 if ( p != NULL ) *p = '\0';
2459 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2461 if (uzbl.comm.sync_stdout)
2462 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2464 g_string_free(s, TRUE);
2468 save_cookies (SoupMessage *msg, gpointer user_data){
2472 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2473 cookie = soup_cookie_to_set_cookie_header(ck->data);
2474 SoupURI * soup_uri = soup_message_get_uri(msg);
2475 GString *s = g_string_new ("");
2476 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2477 run_handler(uzbl.behave.cookie_handler, s->str);
2479 g_string_free(s, TRUE);
2484 /* --- WEBINSPECTOR --- */
2486 hide_window_cb(GtkWidget *widget, gpointer data) {
2489 gtk_widget_hide(widget);
2493 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2496 (void) web_inspector;
2497 GtkWidget* scrolled_window;
2498 GtkWidget* new_web_view;
2501 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2502 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2503 G_CALLBACK(hide_window_cb), NULL);
2505 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2506 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2507 gtk_widget_show(g->inspector_window);
2509 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2510 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2511 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2512 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2513 gtk_widget_show(scrolled_window);
2515 new_web_view = webkit_web_view_new();
2516 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2518 return WEBKIT_WEB_VIEW(new_web_view);
2522 inspector_show_window_cb (WebKitWebInspector* inspector){
2524 gtk_widget_show(uzbl.gui.inspector_window);
2528 /* TODO: Add variables and code to make use of these functions */
2530 inspector_close_window_cb (WebKitWebInspector* inspector){
2536 inspector_attach_window_cb (WebKitWebInspector* inspector){
2542 inspector_detach_window_cb (WebKitWebInspector* inspector){
2548 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2554 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2560 set_up_inspector() {
2562 WebKitWebSettings *settings = view_settings();
2563 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2565 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2566 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2567 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2568 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2569 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2570 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2571 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2573 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2577 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2579 uzbl_cmdprop *c = v;
2584 if(c->type == TYPE_STR)
2585 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2586 else if(c->type == TYPE_INT)
2587 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2588 else if(c->type == TYPE_FLOAT)
2589 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2593 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2597 printf("bind %s = %s %s\n", (char *)k ,
2598 (char *)a->name, a->param?(char *)a->param:"");
2603 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2604 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2608 retreive_geometry() {
2610 GString *buf = g_string_new("");
2612 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2613 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2615 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2617 if(uzbl.gui.geometry)
2618 g_free(uzbl.gui.geometry);
2619 uzbl.gui.geometry = g_string_free(buf, FALSE);
2622 /* set up gtk, gobject, variable defaults and other things that tests and other
2623 * external applications need to do anyhow */
2625 initialize(int argc, char *argv[]) {
2626 gtk_init (&argc, &argv);
2627 if (!g_thread_supported ())
2628 g_thread_init (NULL);
2629 uzbl.state.executable_path = g_strdup(argv[0]);
2630 uzbl.state.selected_url = NULL;
2631 uzbl.state.searchtx = NULL;
2633 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2634 g_option_context_add_main_entries (context, entries, NULL);
2635 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2636 g_option_context_parse (context, &argc, &argv, NULL);
2637 g_option_context_free(context);
2639 if (uzbl.behave.print_version) {
2640 printf("Commit: %s\n", COMMIT);
2644 /* initialize hash table */
2645 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2647 uzbl.net.soup_session = webkit_get_default_session();
2648 uzbl.state.keycmd = g_strdup("");
2650 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2651 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2652 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2653 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2654 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2655 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2657 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2658 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2659 uzbl.gui.sbar.progress_w = 10;
2661 /* HTML mode defaults*/
2662 uzbl.behave.html_buffer = g_string_new("");
2663 uzbl.behave.html_endmarker = g_strdup(".");
2664 uzbl.behave.html_timeout = 60;
2665 uzbl.behave.base_url = g_strdup("http://invalid");
2667 /* default mode indicators */
2668 uzbl.behave.insert_indicator = g_strdup("I");
2669 uzbl.behave.cmd_indicator = g_strdup("C");
2671 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2672 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2673 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2674 uzbl.info.arch = ARCH;
2675 uzbl.info.commit = COMMIT;
2678 make_var_to_name_hash();
2680 uzbl.gui.scrolled_win = create_browser();
2683 #ifndef UZBL_LIBRARY
2686 main (int argc, char* argv[]) {
2687 initialize(argc, argv);
2689 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2693 /* initial packing */
2694 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2695 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2697 if (uzbl.state.socket_id) {
2698 uzbl.gui.plug = create_plug ();
2699 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2700 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2702 uzbl.gui.main_window = create_window ();
2703 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2704 gtk_widget_show_all (uzbl.gui.main_window);
2705 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2708 if(!uzbl.state.instance_name)
2709 uzbl.state.instance_name = itos((int)uzbl.xwin);
2711 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2713 if (uzbl.state.verbose) {
2714 printf("Uzbl start location: %s\n", argv[0]);
2715 if (uzbl.state.socket_id)
2716 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2718 printf("window_id %i\n",(int) uzbl.xwin);
2719 printf("pid %i\n", getpid ());
2720 printf("name: %s\n", uzbl.state.instance_name);
2723 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2724 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2725 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2726 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2727 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2729 if(uzbl.gui.geometry)
2732 retreive_geometry();
2734 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2735 if (argc > 1 && !uzbl.state.uri)
2736 uri_override = g_strdup(argv[1]);
2737 gboolean verbose_override = uzbl.state.verbose;
2740 set_insert_mode(FALSE);
2742 if (!uzbl.behave.show_status)
2743 gtk_widget_hide(uzbl.gui.mainbar);
2750 if (verbose_override > uzbl.state.verbose)
2751 uzbl.state.verbose = verbose_override;
2754 set_var_value("uri", uri_override);
2755 g_free(uri_override);
2756 } else if (uzbl.state.uri)
2762 return EXIT_SUCCESS;
2766 /* vi: set et ts=4: */