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. */
97 /*@null@*/ void (*func)(void);
100 /* abbreviations to help keep the table's width humane */
101 #define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun }
102 #define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun }
103 #define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun }
104 #define PTR_C_STR(var, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun }
105 #define PTR_C_INT(var, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun }
106 #define PTR_C_FLOAT(var, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun }
111 } var_name_to_ptr[] = {
112 /* variable name pointer to variable in code dump callback function */
113 /* ---------------------------------------------------------------------------------------------- */
114 { "uri", PTR_V_STR(uzbl.state.uri, 1, cmd_load_uri)},
115 { "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)},
116 { "mode", PTR_V_INT(uzbl.behave.mode, 0, NULL)},
117 { "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)},
118 { "base_url", PTR_V_STR(uzbl.behave.base_url, 1, NULL)},
119 { "html_endmarker", PTR_V_STR(uzbl.behave.html_endmarker, 1, NULL)},
120 { "html_mode_timeout", PTR_V_INT(uzbl.behave.html_timeout, 1, NULL)},
121 { "keycmd", PTR_V_STR(uzbl.state.keycmd, 1, set_keycmd)},
122 { "status_message", PTR_V_STR(uzbl.gui.sbar.msg, 1, update_title)},
123 { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, cmd_set_status)},
124 { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, move_statusbar)},
125 { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, update_title)},
126 { "status_pbar_done", PTR_V_STR(uzbl.gui.sbar.progress_s, 1, update_title)},
127 { "status_pbar_pending", PTR_V_STR(uzbl.gui.sbar.progress_u, 1, update_title)},
128 { "status_pbar_width", PTR_V_INT(uzbl.gui.sbar.progress_w, 1, update_title)},
129 { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, update_title)},
130 { "insert_indicator", PTR_V_STR(uzbl.behave.insert_indicator, 1, update_indicator)},
131 { "command_indicator", PTR_V_STR(uzbl.behave.cmd_indicator, 1, update_indicator)},
132 { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, update_title)},
133 { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, update_title)},
134 { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)},
135 { "insert_mode", PTR_V_INT(uzbl.behave.insert_mode, 1, set_mode_indicator)},
136 { "always_insert_mode", PTR_V_INT(uzbl.behave.always_insert_mode, 1, cmd_always_insert_mode)},
137 { "reset_command_mode", PTR_V_INT(uzbl.behave.reset_command_mode, 1, NULL)},
138 { "modkey", PTR_V_STR(uzbl.behave.modkey, 1, cmd_modkey)},
139 { "load_finish_handler", PTR_V_STR(uzbl.behave.load_finish_handler, 1, NULL)},
140 { "load_start_handler", PTR_V_STR(uzbl.behave.load_start_handler, 1, NULL)},
141 { "load_commit_handler", PTR_V_STR(uzbl.behave.load_commit_handler, 1, NULL)},
142 { "history_handler", PTR_V_STR(uzbl.behave.history_handler, 1, NULL)},
143 { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)},
144 { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_cookie_handler)},
145 { "new_window", PTR_V_STR(uzbl.behave.new_window, 1, cmd_new_window)},
146 { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)},
147 { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)},
148 { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)},
149 { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)},
150 { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)},
151 { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)},
152 { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)},
153 { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)},
155 /* exported WebKitWebSettings properties */
156 { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)},
157 { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)},
158 { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)},
159 { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)},
160 { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)},
161 { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)},
162 { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)},
163 { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)},
164 { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)},
165 { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)},
166 { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)},
167 { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
168 { "resizable_text_areas",PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
169 { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
170 { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
171 { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
173 /* constants (not dumpable or writeable) */
174 { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)},
175 { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)},
176 { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)},
177 { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)},
178 { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)},
179 { "LOAD_PROGRESS", PTR_C_INT(uzbl.gui.sbar.load_progress, NULL)},
180 { "LOAD_PROGRESSBAR", PTR_C_STR(uzbl.gui.sbar.progress_bar, NULL)},
181 { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)},
182 { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)},
183 { "MODE", PTR_C_STR(uzbl.gui.sbar.mode_indicator, NULL)},
184 { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)},
186 { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
187 }, *n2v_p = var_name_to_ptr;
191 /*@null@*/ char *key;
194 { "SHIFT", GDK_SHIFT_MASK }, // shift
195 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
196 { "CONTROL", GDK_CONTROL_MASK }, // control
197 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
198 { "MOD2", GDK_MOD2_MASK }, // 5th mod
199 { "MOD3", GDK_MOD3_MASK }, // 6th mod
200 { "MOD4", GDK_MOD4_MASK }, // 7th mod
201 { "MOD5", GDK_MOD5_MASK }, // 8th mod
202 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
203 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
204 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
205 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
206 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
207 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
208 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
209 { "META", GDK_META_MASK }, // meta (since 2.10)
214 /* construct a hash from the var_name_to_ptr array for quick access */
216 make_var_to_name_hash() {
217 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
219 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
224 /* --- UTILITY FUNCTIONS --- */
225 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
227 get_exp_type(const gchar *s) {
231 else if(*(s+1) == '{')
232 return EXP_BRACED_VAR;
233 else if(*(s+1) == '<')
235 else if(*(s+1) == '[')
238 return EXP_SIMPLE_VAR;
245 * recurse == 1: don't expand '@(command)@'
246 * recurse == 2: don't expand '@<java script>@'
249 expand(const char *s, guint recurse) {
252 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
256 gchar *cmd_stdout = NULL;
258 GString *buf = g_string_new("");
259 GString *js_ret = g_string_new("");
264 g_string_append_c(buf, *++s);
269 etype = get_exp_type(s);
274 vend = strpbrk(s, end_simple_var);
275 if(!vend) vend = strchr(s, '\0');
279 vend = strchr(s, '}');
280 if(!vend) vend = strchr(s, '\0');
284 vend = strstr(s, ")@");
285 if(!vend) vend = strchr(s, '\0');
289 vend = strstr(s, ">@");
290 if(!vend) vend = strchr(s, '\0');
294 vend = strstr(s, "]@");
295 if(!vend) vend = strchr(s, '\0');
300 ret = g_strndup(s, vend-s);
302 if(etype == EXP_SIMPLE_VAR ||
303 etype == EXP_BRACED_VAR) {
304 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
305 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
306 g_string_append(buf, (gchar *)*c->ptr.s);
307 } else if(c->type == TYPE_INT) {
308 g_string_append_printf(buf, "%d", *c->ptr.i);
310 else if(c->type == TYPE_FLOAT) {
311 g_string_append_printf(buf, "%f", *c->ptr.f);
315 if(etype == EXP_SIMPLE_VAR)
320 else if(recurse != 1 &&
322 mycmd = expand(ret, 1);
323 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
327 g_printerr("error on running command: %s\n", err->message);
330 else if (*cmd_stdout) {
331 size_t len = strlen(cmd_stdout);
333 if(len > 0 && cmd_stdout[len-1] == '\n')
334 cmd_stdout[--len] = '\0'; /* strip trailing newline */
336 g_string_append(buf, cmd_stdout);
341 else if(recurse != 2 &&
343 mycmd = expand(ret, 2);
344 eval_js(uzbl.gui.web_view, mycmd, js_ret);
348 g_string_append(buf, js_ret->str);
349 g_string_free(js_ret, TRUE);
350 js_ret = g_string_new("");
354 else if(etype == EXP_ESCAPE) {
355 mycmd = expand(ret, 0);
356 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
358 g_string_append(buf, escaped);
370 g_string_append_c(buf, *s);
375 g_string_free(js_ret, TRUE);
376 return g_string_free(buf, FALSE);
383 snprintf(tmp, sizeof(tmp), "%i", val);
384 return g_strdup(tmp);
388 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
391 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
394 str_replace (const char* search, const char* replace, const char* string) {
398 buf = g_strsplit (string, search, -1);
399 ret = g_strjoinv (replace, buf);
400 g_strfreev(buf); // somebody said this segfaults
406 read_file_by_line (gchar *path) {
407 GIOChannel *chan = NULL;
408 gchar *readbuf = NULL;
410 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
413 chan = g_io_channel_new_file(path, "r", NULL);
416 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
417 const gchar* val = g_strdup (readbuf);
418 g_array_append_val (lines, val);
423 g_io_channel_unref (chan);
425 fprintf(stderr, "File '%s' not be read.\n", path);
432 parseenv (char* string) {
433 extern char** environ;
434 gchar* tmpstr = NULL;
438 while (environ[i] != NULL) {
439 gchar** env = g_strsplit (environ[i], "=", 2);
440 gchar* envname = g_strconcat ("$", env[0], NULL);
442 if (g_strrstr (string, envname) != NULL) {
443 tmpstr = g_strdup(string);
445 string = str_replace(envname, env[1], tmpstr);
450 g_strfreev (env); // somebody said this breaks uzbl
458 setup_signal(int signr, sigfunc *shandler) {
459 struct sigaction nh, oh;
461 nh.sa_handler = shandler;
462 sigemptyset(&nh.sa_mask);
465 if(sigaction(signr, &nh, &oh) < 0)
473 if (uzbl.behave.fifo_dir)
474 unlink (uzbl.comm.fifo_path);
475 if (uzbl.behave.socket_dir)
476 unlink (uzbl.comm.socket_path);
478 g_free(uzbl.state.executable_path);
479 g_free(uzbl.state.keycmd);
480 g_hash_table_destroy(uzbl.bindings);
481 g_hash_table_destroy(uzbl.behave.commands);
484 /* used for html_mode_timeout
485 * be sure to extend this function to use
486 * more timers if needed in other places
489 set_timeout(int seconds) {
491 memset(&t, 0, sizeof t);
493 t.it_value.tv_sec = seconds;
494 t.it_value.tv_usec = 0;
495 setitimer(ITIMER_REAL, &t, NULL);
498 /* --- SIGNAL HANDLER --- */
501 catch_sigterm(int s) {
507 catch_sigint(int s) {
517 set_var_value("mode", "0");
522 /* --- CALLBACKS --- */
525 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
528 (void) navigation_action;
529 (void) policy_decision;
531 const gchar* uri = webkit_network_request_get_uri (request);
532 if (uzbl.state.verbose)
533 printf("New window requested -> %s \n", uri);
534 webkit_web_policy_decision_use(policy_decision);
539 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
544 /* If we can display it, let's display it... */
545 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
546 webkit_web_policy_decision_use (policy_decision);
550 /* ...everything we can't displayed is downloaded */
551 webkit_web_policy_decision_download (policy_decision);
556 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
560 if (uzbl.state.selected_url != NULL) {
561 if (uzbl.state.verbose)
562 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
563 new_window_load_uri(uzbl.state.selected_url);
565 if (uzbl.state.verbose)
566 printf("New web view -> %s\n","Nothing to open, exiting");
572 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
575 if (uzbl.behave.download_handler) {
576 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
577 if (uzbl.state.verbose)
578 printf("Download -> %s\n",uri);
579 /* if urls not escaped, we may have to escape and quote uri before this call */
580 run_handler(uzbl.behave.download_handler, uri);
585 /* scroll a bar in a given direction */
587 scroll (GtkAdjustment* bar, GArray *argv) {
591 gdouble page_size = gtk_adjustment_get_page_size(bar);
592 gdouble value = gtk_adjustment_get_value(bar);
593 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
596 value += page_size * amount * 0.01;
600 max_value = gtk_adjustment_get_upper(bar) - page_size;
602 if (value > max_value)
603 value = max_value; /* don't scroll past the end of the page */
605 gtk_adjustment_set_value (bar, value);
609 scroll_begin(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_lower(uzbl.gui.bar_v));
615 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
616 (void) page; (void) argv; (void) result;
617 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
618 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
622 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
623 (void) page; (void) result;
624 scroll(uzbl.gui.bar_v, argv);
628 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
629 (void) page; (void) result;
630 scroll(uzbl.gui.bar_h, argv);
635 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
636 if(uzbl.state.verbose)
637 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
639 /* update geometry var with the actual geometry
640 this is necessary as some WMs don't seem to honour
641 the above setting and we don't want to end up with
642 wrong geometry information
649 if (!uzbl.behave.show_status) {
650 gtk_widget_hide(uzbl.gui.mainbar);
652 gtk_widget_show(uzbl.gui.mainbar);
658 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
663 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
667 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
672 if (uzbl.behave.show_status) {
673 gtk_widget_hide(uzbl.gui.mainbar);
675 gtk_widget_show(uzbl.gui.mainbar);
677 uzbl.behave.show_status = !uzbl.behave.show_status;
682 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
686 //Set selected_url state variable
687 g_free(uzbl.state.selected_url);
688 uzbl.state.selected_url = NULL;
690 uzbl.state.selected_url = g_strdup(link);
696 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
699 const gchar *title = webkit_web_view_get_title(web_view);
700 if (uzbl.gui.main_title)
701 g_free (uzbl.gui.main_title);
702 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
707 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
710 uzbl.gui.sbar.load_progress = progress;
712 g_free(uzbl.gui.sbar.progress_bar);
713 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
719 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
723 if (uzbl.behave.load_finish_handler)
724 run_handler(uzbl.behave.load_finish_handler, "");
727 void clear_keycmd() {
728 g_free(uzbl.state.keycmd);
729 uzbl.state.keycmd = g_strdup("");
733 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
737 uzbl.gui.sbar.load_progress = 0;
738 clear_keycmd(); // don't need old commands to remain on new page?
739 if (uzbl.behave.load_start_handler)
740 run_handler(uzbl.behave.load_start_handler, "");
744 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
747 g_free (uzbl.state.uri);
748 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
749 uzbl.state.uri = g_string_free (newuri, FALSE);
750 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
751 set_insert_mode(uzbl.behave.always_insert_mode);
754 if (uzbl.behave.load_commit_handler)
755 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
759 destroy_cb (GtkWidget* widget, gpointer data) {
767 if (uzbl.behave.history_handler) {
769 struct tm * timeinfo;
772 timeinfo = localtime ( &rawtime );
773 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
774 run_handler(uzbl.behave.history_handler, date);
779 /* VIEW funcs (little webkit wrappers) */
780 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
782 VIEWFUNC(reload_bypass_cache)
783 VIEWFUNC(stop_loading)
790 /* -- command to callback/function map for things we cannot attach to any signals */
791 struct {char *key; CommandInfo value;} cmdlist[] =
792 { /* key function no_split */
793 { "back", {view_go_back, 0} },
794 { "forward", {view_go_forward, 0} },
795 { "scroll_vert", {scroll_vert, 0} },
796 { "scroll_horz", {scroll_horz, 0} },
797 { "scroll_begin", {scroll_begin, 0} },
798 { "scroll_end", {scroll_end, 0} },
799 { "reload", {view_reload, 0}, },
800 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
801 { "stop", {view_stop_loading, 0}, },
802 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
803 { "zoom_out", {view_zoom_out, 0}, },
804 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
805 { "uri", {load_uri, TRUE} },
806 { "js", {run_js, TRUE} },
807 { "script", {run_external_js, 0} },
808 { "toggle_status", {toggle_status_cb, 0} },
809 { "spawn", {spawn, 0} },
810 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
811 { "sh", {spawn_sh, 0} },
812 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
813 { "exit", {close_uzbl, 0} },
814 { "search", {search_forward_text, TRUE} },
815 { "search_reverse", {search_reverse_text, TRUE} },
816 { "dehilight", {dehilight, 0} },
817 { "toggle_insert_mode", {toggle_insert_mode, 0} },
818 { "set", {set_var, TRUE} },
819 //{ "get", {get_var, TRUE} },
820 { "bind", {act_bind, TRUE} },
821 { "dump_config", {act_dump_config, 0} },
822 { "keycmd", {keycmd, TRUE} },
823 { "keycmd_nl", {keycmd_nl, TRUE} },
824 { "keycmd_bs", {keycmd_bs, 0} },
825 { "chain", {chain, 0} },
826 { "print", {print, TRUE} }
833 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
835 for (i = 0; i < LENGTH(cmdlist); i++)
836 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
839 /* -- CORE FUNCTIONS -- */
842 free_action(gpointer act) {
843 Action *action = (Action*)act;
844 g_free(action->name);
846 g_free(action->param);
851 new_action(const gchar *name, const gchar *param) {
852 Action *action = g_new(Action, 1);
854 action->name = g_strdup(name);
856 action->param = g_strdup(param);
858 action->param = NULL;
864 file_exists (const char * filename) {
865 return (access(filename, F_OK) == 0);
869 set_var(WebKitWebView *page, GArray *argv, GString *result) {
870 (void) page; (void) result;
871 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
872 if (split[0] != NULL) {
873 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
874 set_var_value(g_strstrip(split[0]), value);
881 print(WebKitWebView *page, GArray *argv, GString *result) {
882 (void) page; (void) result;
885 buf = expand(argv_idx(argv, 0), 0);
886 g_string_assign(result, buf);
891 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
892 (void) page; (void) result;
893 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
894 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
895 add_binding(g_strstrip(split[0]), value);
913 set_mode_indicator() {
914 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
915 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
920 set_mode_indicator();
925 set_insert_mode(gboolean mode) {
926 uzbl.behave.insert_mode = mode;
927 set_mode_indicator();
931 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
932 (void) page; (void) result;
934 if (argv_idx(argv, 0)) {
935 if (strcmp (argv_idx(argv, 0), "0") == 0) {
936 set_insert_mode(FALSE);
938 set_insert_mode(TRUE);
941 set_insert_mode( !uzbl.behave.insert_mode );
948 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
951 if (argv_idx(argv, 0)) {
952 GString* newuri = g_string_new (argv_idx(argv, 0));
953 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
954 run_js(web_view, argv, NULL);
957 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
958 g_string_prepend (newuri, "http://");
959 /* if we do handle cookies, ask our handler for them */
960 webkit_web_view_load_uri (web_view, newuri->str);
961 g_string_free (newuri, TRUE);
968 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
969 size_t argumentCount, const JSValueRef arguments[],
970 JSValueRef* exception) {
975 JSStringRef js_result_string;
976 GString *result = g_string_new("");
978 if (argumentCount >= 1) {
979 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
980 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
981 char ctl_line[arg_size];
982 JSStringGetUTF8CString(arg, ctl_line, arg_size);
984 parse_cmd_line(ctl_line, result);
986 JSStringRelease(arg);
988 js_result_string = JSStringCreateWithUTF8CString(result->str);
990 g_string_free(result, TRUE);
992 return JSValueMakeString(ctx, js_result_string);
995 JSStaticFunction js_static_functions[] = {
996 {"run", js_run_command, kJSPropertyAttributeNone},
1001 /* This function creates the class and its definition, only once */
1002 if (!uzbl.js.initialized) {
1003 /* it would be pretty cool to make this dynamic */
1004 uzbl.js.classdef = kJSClassDefinitionEmpty;
1005 uzbl.js.classdef.staticFunctions = js_static_functions;
1007 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1013 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1014 WebKitWebFrame *frame;
1015 JSGlobalContextRef context;
1016 JSObjectRef globalobject;
1017 JSStringRef var_name;
1019 JSStringRef js_script;
1020 JSValueRef js_result;
1021 JSStringRef js_result_string;
1022 size_t js_result_size;
1026 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1027 context = webkit_web_frame_get_global_context(frame);
1028 globalobject = JSContextGetGlobalObject(context);
1030 /* uzbl javascript namespace */
1031 var_name = JSStringCreateWithUTF8CString("Uzbl");
1032 JSObjectSetProperty(context, globalobject, var_name,
1033 JSObjectMake(context, uzbl.js.classref, NULL),
1034 kJSClassAttributeNone, NULL);
1036 /* evaluate the script and get return value*/
1037 js_script = JSStringCreateWithUTF8CString(script);
1038 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1039 if (js_result && !JSValueIsUndefined(context, js_result)) {
1040 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1041 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1043 if (js_result_size) {
1044 char js_result_utf8[js_result_size];
1045 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1046 g_string_assign(result, js_result_utf8);
1049 JSStringRelease(js_result_string);
1053 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1055 JSStringRelease(var_name);
1056 JSStringRelease(js_script);
1060 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1061 if (argv_idx(argv, 0))
1062 eval_js(web_view, argv_idx(argv, 0), result);
1066 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1068 if (argv_idx(argv, 0)) {
1069 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1074 while ((line = g_array_index(lines, gchar*, i))) {
1076 js = g_strdup (line);
1078 gchar* newjs = g_strconcat (js, line, NULL);
1085 if (uzbl.state.verbose)
1086 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1088 if (argv_idx (argv, 1)) {
1089 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1093 eval_js (web_view, js, result);
1095 g_array_free (lines, TRUE);
1100 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1101 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1102 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1103 webkit_web_view_unmark_text_matches (page);
1104 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1105 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1109 if (uzbl.state.searchtx) {
1110 if (uzbl.state.verbose)
1111 printf ("Searching: %s\n", uzbl.state.searchtx);
1112 webkit_web_view_set_highlight_text_matches (page, TRUE);
1113 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1118 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1120 search_text(page, argv, TRUE);
1124 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1126 search_text(page, argv, FALSE);
1130 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1131 (void) argv; (void) result;
1132 webkit_web_view_set_highlight_text_matches (page, FALSE);
1137 new_window_load_uri (const gchar * uri) {
1138 if (uzbl.behave.new_window) {
1139 GString *s = g_string_new ("");
1140 g_string_printf(s, "'%s'", uri);
1141 run_handler(uzbl.behave.new_window, s->str);
1144 GString* to_execute = g_string_new ("");
1145 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1147 for (i = 0; entries[i].long_name != NULL; i++) {
1148 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1149 gchar** str = (gchar**)entries[i].arg_data;
1151 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1155 if (uzbl.state.verbose)
1156 printf("\n%s\n", to_execute->str);
1157 g_spawn_command_line_async (to_execute->str, NULL);
1158 g_string_free (to_execute, TRUE);
1162 chain (WebKitWebView *page, GArray *argv, GString *result) {
1163 (void) page; (void) result;
1165 gchar **parts = NULL;
1167 while ((a = argv_idx(argv, i++))) {
1168 parts = g_strsplit (a, " ", 2);
1170 parse_command(parts[0], parts[1], result);
1176 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1180 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1186 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1190 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1196 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1201 int len = strlen(uzbl.state.keycmd);
1202 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1204 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1209 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1216 /* --Statusbar functions-- */
1218 build_progressbar_ascii(int percent) {
1219 int width=uzbl.gui.sbar.progress_w;
1222 GString *bar = g_string_new("");
1224 l = (double)percent*((double)width/100.);
1225 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1227 for(i=0; i<(int)l; i++)
1228 g_string_append(bar, uzbl.gui.sbar.progress_s);
1231 g_string_append(bar, uzbl.gui.sbar.progress_u);
1233 return g_string_free(bar, FALSE);
1235 /* --End Statusbar functions-- */
1238 sharg_append(GArray *a, const gchar *str) {
1239 const gchar *s = (str ? str : "");
1240 g_array_append_val(a, s);
1243 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1245 run_command (const gchar *command, const guint npre, const gchar **args,
1246 const gboolean sync, char **output_stdout) {
1247 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1250 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1251 gchar *pid = itos(getpid());
1252 gchar *xwin = itos(uzbl.xwin);
1254 sharg_append(a, command);
1255 for (i = 0; i < npre; i++) /* add n args before the default vars */
1256 sharg_append(a, args[i]);
1257 sharg_append(a, uzbl.state.config_file);
1258 sharg_append(a, pid);
1259 sharg_append(a, xwin);
1260 sharg_append(a, uzbl.comm.fifo_path);
1261 sharg_append(a, uzbl.comm.socket_path);
1262 sharg_append(a, uzbl.state.uri);
1263 sharg_append(a, uzbl.gui.main_title);
1265 for (i = npre; i < g_strv_length((gchar**)args); i++)
1266 sharg_append(a, args[i]);
1270 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1272 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1273 NULL, NULL, output_stdout, NULL, NULL, &err);
1274 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1275 NULL, NULL, NULL, &err);
1277 if (uzbl.state.verbose) {
1278 GString *s = g_string_new("spawned:");
1279 for (i = 0; i < (a->len); i++) {
1280 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1281 g_string_append_printf(s, " %s", qarg);
1284 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1285 printf("%s\n", s->str);
1286 g_string_free(s, TRUE);
1288 printf("Stdout: %s\n", *output_stdout);
1292 g_printerr("error on run_command: %s\n", err->message);
1297 g_array_free (a, TRUE);
1302 split_quoted(const gchar* src, const gboolean unquote) {
1303 /* split on unquoted space, return array of strings;
1304 remove a layer of quotes and backslashes if unquote */
1305 if (!src) return NULL;
1307 gboolean dq = FALSE;
1308 gboolean sq = FALSE;
1309 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1310 GString *s = g_string_new ("");
1314 for (p = src; *p != '\0'; p++) {
1315 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1316 else if (*p == '\\') { g_string_append_c(s, *p++);
1317 g_string_append_c(s, *p); }
1318 else if ((*p == '"') && unquote && !sq) dq = !dq;
1319 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1321 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1322 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1324 else if ((*p == ' ') && !dq && !sq) {
1325 dup = g_strdup(s->str);
1326 g_array_append_val(a, dup);
1327 g_string_truncate(s, 0);
1328 } else g_string_append_c(s, *p);
1330 dup = g_strdup(s->str);
1331 g_array_append_val(a, dup);
1332 ret = (gchar**)a->data;
1333 g_array_free (a, FALSE);
1334 g_string_free (s, TRUE);
1339 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1340 (void)web_view; (void)result;
1341 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1342 if (argv_idx(argv, 0))
1343 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1347 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1348 (void)web_view; (void)result;
1350 if (argv_idx(argv, 0))
1351 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1352 TRUE, &uzbl.comm.sync_stdout);
1356 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1357 (void)web_view; (void)result;
1358 if (!uzbl.behave.shell_cmd) {
1359 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1364 gchar *spacer = g_strdup("");
1365 g_array_insert_val(argv, 1, spacer);
1366 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1368 for (i = 1; i < g_strv_length(cmd); i++)
1369 g_array_prepend_val(argv, cmd[i]);
1371 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1377 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1378 (void)web_view; (void)result;
1379 if (!uzbl.behave.shell_cmd) {
1380 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1385 gchar *spacer = g_strdup("");
1386 g_array_insert_val(argv, 1, spacer);
1387 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1389 for (i = 1; i < g_strv_length(cmd); i++)
1390 g_array_prepend_val(argv, cmd[i]);
1392 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1393 TRUE, &uzbl.comm.sync_stdout);
1399 parse_command(const char *cmd, const char *param, GString *result) {
1402 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1404 gchar **par = split_quoted(param, TRUE);
1405 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1407 if (c->no_split) { /* don't split */
1408 sharg_append(a, param);
1410 for (i = 0; i < g_strv_length(par); i++)
1411 sharg_append(a, par[i]);
1414 if (result == NULL) {
1415 GString *result_print = g_string_new("");
1417 c->function(uzbl.gui.web_view, a, result_print);
1418 if (result_print->len)
1419 printf("%*s\n", (int)result_print->len, result_print->str);
1421 g_string_free(result_print, TRUE);
1423 c->function(uzbl.gui.web_view, a, result);
1426 g_array_free (a, TRUE);
1429 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1436 if(*uzbl.net.proxy_url == ' '
1437 || uzbl.net.proxy_url == NULL) {
1438 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1439 (GType) SOUP_SESSION_PROXY_URI);
1442 suri = soup_uri_new(uzbl.net.proxy_url);
1443 g_object_set(G_OBJECT(uzbl.net.soup_session),
1444 SOUP_SESSION_PROXY_URI,
1446 soup_uri_free(suri);
1453 if(file_exists(uzbl.gui.icon)) {
1454 if (uzbl.gui.main_window)
1455 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1457 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1463 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1464 g_array_append_val (a, uzbl.state.uri);
1465 load_uri(uzbl.gui.web_view, a, NULL);
1466 g_array_free (a, TRUE);
1470 cmd_always_insert_mode() {
1471 set_insert_mode(uzbl.behave.always_insert_mode);
1477 g_object_set(G_OBJECT(uzbl.net.soup_session),
1478 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1482 cmd_max_conns_host() {
1483 g_object_set(G_OBJECT(uzbl.net.soup_session),
1484 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1489 soup_session_remove_feature
1490 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1491 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1492 /*g_free(uzbl.net.soup_logger);*/
1494 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1495 soup_session_add_feature(uzbl.net.soup_session,
1496 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1501 return webkit_web_view_get_settings(uzbl.gui.web_view);
1506 WebKitWebSettings *ws = view_settings();
1507 if (uzbl.behave.font_size > 0) {
1508 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1511 if (uzbl.behave.monospace_size > 0) {
1512 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1513 uzbl.behave.monospace_size, NULL);
1515 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1516 uzbl.behave.font_size, NULL);
1522 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1526 cmd_disable_plugins() {
1527 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1528 !uzbl.behave.disable_plugins, NULL);
1532 cmd_disable_scripts() {
1533 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1534 !uzbl.behave.disable_scripts, NULL);
1538 cmd_minimum_font_size() {
1539 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1540 uzbl.behave.minimum_font_size, NULL);
1543 cmd_autoload_img() {
1544 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1545 uzbl.behave.autoload_img, NULL);
1550 cmd_autoshrink_img() {
1551 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1552 uzbl.behave.autoshrink_img, NULL);
1557 cmd_enable_spellcheck() {
1558 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1559 uzbl.behave.enable_spellcheck, NULL);
1563 cmd_enable_private() {
1564 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1565 uzbl.behave.enable_private, NULL);
1570 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1571 uzbl.behave.print_bg, NULL);
1576 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1577 uzbl.behave.style_uri, NULL);
1581 cmd_resizable_txt() {
1582 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1583 uzbl.behave.resizable_txt, NULL);
1587 cmd_default_encoding() {
1588 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1589 uzbl.behave.default_encoding, NULL);
1593 cmd_enforce_96dpi() {
1594 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1595 uzbl.behave.enforce_96dpi, NULL);
1599 cmd_caret_browsing() {
1600 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1601 uzbl.behave.caret_browsing, NULL);
1605 cmd_cookie_handler() {
1606 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1607 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1608 if ((g_strcmp0(split[0], "sh") == 0) ||
1609 (g_strcmp0(split[0], "spawn") == 0)) {
1610 g_free (uzbl.behave.cookie_handler);
1611 uzbl.behave.cookie_handler =
1612 g_strdup_printf("sync_%s %s", split[0], split[1]);
1619 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1620 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1621 if ((g_strcmp0(split[0], "sh") == 0) ||
1622 (g_strcmp0(split[0], "spawn") == 0)) {
1623 g_free (uzbl.behave.new_window);
1624 uzbl.behave.new_window =
1625 g_strdup_printf("%s %s", split[0], split[1]);
1632 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1637 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1642 if(uzbl.behave.inject_html) {
1643 webkit_web_view_load_html_string (uzbl.gui.web_view,
1644 uzbl.behave.inject_html, NULL);
1653 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1654 uzbl.behave.modmask = 0;
1656 if(uzbl.behave.modkey)
1657 g_free(uzbl.behave.modkey);
1658 uzbl.behave.modkey = buf;
1660 for (i = 0; modkeys[i].key != NULL; i++) {
1661 if (g_strrstr(buf, modkeys[i].key))
1662 uzbl.behave.modmask |= modkeys[i].mask;
1668 if (*uzbl.net.useragent == ' ') {
1669 g_free (uzbl.net.useragent);
1670 uzbl.net.useragent = NULL;
1672 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1673 uzbl.net.useragent, NULL);
1679 gtk_widget_ref(uzbl.gui.scrolled_win);
1680 gtk_widget_ref(uzbl.gui.mainbar);
1681 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1682 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1684 if(uzbl.behave.status_top) {
1685 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1686 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1689 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1690 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1692 gtk_widget_unref(uzbl.gui.scrolled_win);
1693 gtk_widget_unref(uzbl.gui.mainbar);
1694 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1699 set_var_value(gchar *name, gchar *val) {
1700 uzbl_cmdprop *c = NULL;
1704 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1705 if(!c->writeable) return FALSE;
1707 /* check for the variable type */
1708 if (c->type == TYPE_STR) {
1709 buf = expand(val, 0);
1712 } else if(c->type == TYPE_INT) {
1713 buf = expand(val, 0);
1714 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1716 } else if (c->type == TYPE_FLOAT) {
1717 buf = expand(val, 0);
1718 *c->ptr.f = strtod(buf, &endp);
1722 /* invoke a command specific function */
1723 if(c->func) c->func();
1730 Behaviour *b = &uzbl.behave;
1732 if(b->html_buffer->str) {
1733 webkit_web_view_load_html_string (uzbl.gui.web_view,
1734 b->html_buffer->str, b->base_url);
1735 g_string_free(b->html_buffer, TRUE);
1736 b->html_buffer = g_string_new("");
1740 enum {M_CMD, M_HTML};
1742 parse_cmd_line(const char *ctl_line, GString *result) {
1743 Behaviour *b = &uzbl.behave;
1746 if(b->mode == M_HTML) {
1747 len = strlen(b->html_endmarker);
1748 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1749 if(len == strlen(ctl_line)-1 &&
1750 !strncmp(b->html_endmarker, ctl_line, len)) {
1752 set_var_value("mode", "0");
1757 set_timeout(b->html_timeout);
1758 g_string_append(b->html_buffer, ctl_line);
1761 else if((ctl_line[0] == '#') /* Comments */
1762 || (ctl_line[0] == ' ')
1763 || (ctl_line[0] == '\n'))
1764 ; /* ignore these lines */
1765 else { /* parse a command */
1767 gchar **tokens = NULL;
1768 len = strlen(ctl_line);
1770 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1771 ctlstrip = g_strndup(ctl_line, len - 1);
1772 else ctlstrip = g_strdup(ctl_line);
1774 tokens = g_strsplit(ctlstrip, " ", 2);
1775 parse_command(tokens[0], tokens[1], result);
1782 build_stream_name(int type, const gchar* dir) {
1783 State *s = &uzbl.state;
1787 str = g_strdup_printf
1788 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1789 } else if (type == SOCKET) {
1790 str = g_strdup_printf
1791 ("%s/uzbl_socket_%s", dir, s->instance_name);
1797 control_fifo(GIOChannel *gio, GIOCondition condition) {
1798 if (uzbl.state.verbose)
1799 printf("triggered\n");
1804 if (condition & G_IO_HUP)
1805 g_error ("Fifo: Read end of pipe died!\n");
1808 g_error ("Fifo: GIOChannel broke\n");
1810 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1811 if (ret == G_IO_STATUS_ERROR) {
1812 g_error ("Fifo: Error reading: %s\n", err->message);
1816 parse_cmd_line(ctl_line, NULL);
1823 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1824 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1825 if (unlink(uzbl.comm.fifo_path) == -1)
1826 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1827 g_free(uzbl.comm.fifo_path);
1828 uzbl.comm.fifo_path = NULL;
1831 GIOChannel *chan = NULL;
1832 GError *error = NULL;
1833 gchar *path = build_stream_name(FIFO, dir);
1835 if (!file_exists(path)) {
1836 if (mkfifo (path, 0666) == 0) {
1837 // 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.
1838 chan = g_io_channel_new_file(path, "r+", &error);
1840 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1841 if (uzbl.state.verbose)
1842 printf ("init_fifo: created successfully as %s\n", path);
1843 uzbl.comm.fifo_path = path;
1845 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1846 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1847 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1848 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1850 /* if we got this far, there was an error; cleanup */
1851 if (error) g_error_free (error);
1858 control_stdin(GIOChannel *gio, GIOCondition condition) {
1860 gchar *ctl_line = NULL;
1863 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1864 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1867 parse_cmd_line(ctl_line, NULL);
1875 GIOChannel *chan = NULL;
1876 GError *error = NULL;
1878 chan = g_io_channel_unix_new(fileno(stdin));
1880 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1881 g_error ("Stdin: could not add watch\n");
1883 if (uzbl.state.verbose)
1884 printf ("Stdin: watch added successfully\n");
1887 g_error ("Stdin: Error while opening: %s\n", error->message);
1889 if (error) g_error_free (error);
1893 control_socket(GIOChannel *chan) {
1894 struct sockaddr_un remote;
1895 unsigned int t = sizeof(remote);
1897 GIOChannel *clientchan;
1899 clientsock = accept (g_io_channel_unix_get_fd(chan),
1900 (struct sockaddr *) &remote, &t);
1902 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1903 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1904 (GIOFunc) control_client_socket, clientchan);
1911 control_client_socket(GIOChannel *clientchan) {
1913 GString *result = g_string_new("");
1914 GError *error = NULL;
1918 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1919 if (ret == G_IO_STATUS_ERROR) {
1920 g_warning ("Error reading: %s\n", error->message);
1921 g_io_channel_shutdown(clientchan, TRUE, &error);
1923 } else if (ret == G_IO_STATUS_EOF) {
1924 /* shutdown and remove channel watch from main loop */
1925 g_io_channel_shutdown(clientchan, TRUE, &error);
1930 parse_cmd_line (ctl_line, result);
1931 g_string_append_c(result, '\n');
1932 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1934 if (ret == G_IO_STATUS_ERROR) {
1935 g_warning ("Error writing: %s", error->message);
1937 g_io_channel_flush(clientchan, &error);
1940 if (error) g_error_free (error);
1941 g_string_free(result, TRUE);
1947 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1948 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1949 if (unlink(uzbl.comm.socket_path) == -1)
1950 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1951 g_free(uzbl.comm.socket_path);
1952 uzbl.comm.socket_path = NULL;
1960 GIOChannel *chan = NULL;
1962 struct sockaddr_un local;
1963 gchar *path = build_stream_name(SOCKET, dir);
1965 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1967 local.sun_family = AF_UNIX;
1968 strcpy (local.sun_path, path);
1969 unlink (local.sun_path);
1971 len = strlen (local.sun_path) + sizeof (local.sun_family);
1972 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1973 if (uzbl.state.verbose)
1974 printf ("init_socket: opened in %s\n", path);
1977 if( (chan = g_io_channel_unix_new(sock)) ) {
1978 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1979 uzbl.comm.socket_path = path;
1982 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1984 /* if we got this far, there was an error; cleanup */
1991 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1992 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1994 // this function may be called very early when the templates are not set (yet), hence the checks
1996 update_title (void) {
1997 Behaviour *b = &uzbl.behave;
2000 if (b->show_status) {
2001 if (b->title_format_short) {
2002 parsed = expand(b->title_format_short, 0);
2003 if (uzbl.gui.main_window)
2004 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2007 if (b->status_format) {
2008 parsed = expand(b->status_format, 0);
2009 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2012 if (b->status_background) {
2014 gdk_color_parse (b->status_background, &color);
2015 //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)
2016 if (uzbl.gui.main_window)
2017 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2018 else if (uzbl.gui.plug)
2019 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2022 if (b->title_format_long) {
2023 parsed = expand(b->title_format_long, 0);
2024 if (uzbl.gui.main_window)
2025 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2032 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2036 retreive_geometry();
2041 key_press_cb (GtkWidget* window, GdkEventKey* event)
2043 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2047 if (event->type != GDK_KEY_PRESS ||
2048 event->keyval == GDK_Page_Up ||
2049 event->keyval == GDK_Page_Down ||
2050 event->keyval == GDK_Up ||
2051 event->keyval == GDK_Down ||
2052 event->keyval == GDK_Left ||
2053 event->keyval == GDK_Right ||
2054 event->keyval == GDK_Shift_L ||
2055 event->keyval == GDK_Shift_R)
2058 /* turn off insert mode (if always_insert_mode is not used) */
2059 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2060 set_insert_mode(uzbl.behave.always_insert_mode);
2065 if (uzbl.behave.insert_mode &&
2066 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2067 (!uzbl.behave.modmask)
2072 if (event->keyval == GDK_Escape) {
2075 dehilight(uzbl.gui.web_view, NULL, NULL);
2079 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2080 if (event->keyval == GDK_Insert) {
2082 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2083 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2085 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2088 GString* keycmd = g_string_new(uzbl.state.keycmd);
2089 g_string_append (keycmd, str);
2090 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2097 if (event->keyval == GDK_BackSpace)
2098 keycmd_bs(NULL, NULL, NULL);
2100 gboolean key_ret = FALSE;
2101 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2104 GString* keycmd = g_string_new(uzbl.state.keycmd);
2105 g_string_append(keycmd, event->string);
2106 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2109 run_keycmd(key_ret);
2111 if (key_ret) return (!uzbl.behave.insert_mode);
2116 run_keycmd(const gboolean key_ret) {
2117 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2119 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2121 parse_command(act->name, act->param, NULL);
2125 /* try if it's an incremental keycmd or one that takes args, and run it */
2126 GString* short_keys = g_string_new ("");
2127 GString* short_keys_inc = g_string_new ("");
2129 guint len = strlen(uzbl.state.keycmd);
2130 for (i=0; i<len; i++) {
2131 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2132 g_string_assign(short_keys_inc, short_keys->str);
2133 g_string_append_c(short_keys, '_');
2134 g_string_append_c(short_keys_inc, '*');
2136 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2137 /* run normal cmds only if return was pressed */
2138 exec_paramcmd(act, i);
2141 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2142 if (key_ret) /* just quit the incremental command on return */
2144 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2148 g_string_truncate(short_keys, short_keys->len - 1);
2150 g_string_free (short_keys, TRUE);
2151 g_string_free (short_keys_inc, TRUE);
2155 exec_paramcmd(const Action *act, const guint i) {
2156 GString *parampart = g_string_new (uzbl.state.keycmd);
2157 GString *actionname = g_string_new ("");
2158 GString *actionparam = g_string_new ("");
2159 g_string_erase (parampart, 0, i+1);
2161 g_string_printf (actionname, act->name, parampart->str);
2163 g_string_printf (actionparam, act->param, parampart->str);
2164 parse_command(actionname->str, actionparam->str, NULL);
2165 g_string_free(actionname, TRUE);
2166 g_string_free(actionparam, TRUE);
2167 g_string_free(parampart, TRUE);
2175 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2176 //main_window_ref = g_object_ref(scrolled_window);
2177 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
2179 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2180 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2182 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2183 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2184 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2189 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2190 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2191 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2192 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2194 return scrolled_window;
2201 g->mainbar = gtk_hbox_new (FALSE, 0);
2203 /* keep a reference to the bar so we can re-pack it at runtime*/
2204 //sbar_ref = g_object_ref(g->mainbar);
2206 g->mainbar_label = gtk_label_new ("");
2207 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2208 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2209 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2210 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2211 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2212 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2218 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2219 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2220 gtk_widget_set_name (window, "Uzbl browser");
2221 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2222 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2223 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2230 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2231 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2232 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2239 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2241 If actname is one that calls an external command, this function will inject
2242 newargs in front of the user-provided args in that command line. They will
2243 come become after the body of the script (in sh) or after the name of
2244 the command to execute (in spawn).
2245 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2246 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2248 The return value consist of two strings: the action (sh, ...) and its args.
2250 If act is not one that calls an external command, then the given action merely
2253 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2254 /* Arrr! Here be memory leaks */
2255 gchar *actdup = g_strdup(actname);
2256 g_array_append_val(rets, actdup);
2258 if ((g_strcmp0(actname, "spawn") == 0) ||
2259 (g_strcmp0(actname, "sh") == 0) ||
2260 (g_strcmp0(actname, "sync_spawn") == 0) ||
2261 (g_strcmp0(actname, "sync_sh") == 0)) {
2263 GString *a = g_string_new("");
2264 gchar **spawnparts = split_quoted(origargs, FALSE);
2265 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2266 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2268 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2269 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2271 g_array_append_val(rets, a->str);
2272 g_string_free(a, FALSE);
2273 g_strfreev(spawnparts);
2275 gchar *origdup = g_strdup(origargs);
2276 g_array_append_val(rets, origdup);
2278 return (gchar**)g_array_free(rets, FALSE);
2282 run_handler (const gchar *act, const gchar *args) {
2283 /* Consider this code a temporary hack to make the handlers usable.
2284 In practice, all this splicing, injection, and reconstruction is
2285 inefficient, annoying and hard to manage. Potential pitfalls arise
2286 when the handler specific args 1) are not quoted (the handler
2287 callbacks should take care of this) 2) are quoted but interfere
2288 with the users' own quotation. A more ideal solution is
2289 to refactor parse_command so that it doesn't just take a string
2290 and execute it; rather than that, we should have a function which
2291 returns the argument vector parsed from the string. This vector
2292 could be modified (e.g. insert additional args into it) before
2293 passing it to the next function that actually executes it. Though
2294 it still isn't perfect for chain actions.. will reconsider & re-
2295 factor when I have the time. -duc */
2297 char **parts = g_strsplit(act, " ", 2);
2299 if (g_strcmp0(parts[0], "chain") == 0) {
2300 GString *newargs = g_string_new("");
2301 gchar **chainparts = split_quoted(parts[1], FALSE);
2303 /* for every argument in the chain, inject the handler args
2304 and make sure the new parts are wrapped in quotes */
2305 gchar **cp = chainparts;
2307 gchar *quotless = NULL;
2308 gchar **spliced_quotless = NULL; // sigh -_-;
2309 gchar **inpart = NULL;
2312 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2314 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2315 } else quotless = g_strdup(*cp);
2317 spliced_quotless = g_strsplit(quotless, " ", 2);
2318 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2319 g_strfreev(spliced_quotless);
2321 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2327 parse_command(parts[0], &(newargs->str[1]), NULL);
2328 g_string_free(newargs, TRUE);
2329 g_strfreev(chainparts);
2332 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2333 parse_command(inparts[0], inparts[1], NULL);
2341 add_binding (const gchar *key, const gchar *act) {
2342 char **parts = g_strsplit(act, " ", 2);
2349 if (uzbl.state.verbose)
2350 printf ("Binding %-10s : %s\n", key, act);
2351 action = new_action(parts[0], parts[1]);
2353 if (g_hash_table_remove (uzbl.bindings, key))
2354 g_warning ("Overwriting existing binding for \"%s\"", key);
2355 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2360 get_xdg_var (XDG_Var xdg) {
2361 const gchar* actual_value = getenv (xdg.environmental);
2362 const gchar* home = getenv ("HOME");
2363 gchar* return_value;
2365 if (! actual_value || strcmp (actual_value, "") == 0) {
2366 if (xdg.default_value) {
2367 return_value = str_replace ("~", home, xdg.default_value);
2369 return_value = NULL;
2372 return_value = str_replace("~", home, actual_value);
2375 return return_value;
2379 find_xdg_file (int xdg_type, char* filename) {
2380 /* xdg_type = 0 => config
2381 xdg_type = 1 => data
2382 xdg_type = 2 => cache*/
2384 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2385 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2388 gchar* temporary_string;
2392 if (! file_exists (temporary_file) && xdg_type != 2) {
2393 buf = get_xdg_var (XDG[3 + xdg_type]);
2394 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2397 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2398 g_free (temporary_file);
2399 temporary_file = g_strconcat (temporary_string, filename, NULL);
2403 //g_free (temporary_string); - segfaults.
2405 if (file_exists (temporary_file)) {
2406 return temporary_file;
2413 State *s = &uzbl.state;
2414 Network *n = &uzbl.net;
2416 for (i = 0; default_config[i].command != NULL; i++) {
2417 parse_cmd_line(default_config[i].command, NULL);
2420 if (g_strcmp0(s->config_file, "-") == 0) {
2421 s->config_file = NULL;
2425 else if (!s->config_file) {
2426 s->config_file = find_xdg_file (0, "/uzbl/config");
2429 if (s->config_file) {
2430 GArray* lines = read_file_by_line (s->config_file);
2434 while ((line = g_array_index(lines, gchar*, i))) {
2435 parse_cmd_line (line, NULL);
2439 g_array_free (lines, TRUE);
2441 if (uzbl.state.verbose)
2442 printf ("No configuration file loaded.\n");
2445 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2448 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2451 if (!uzbl.behave.cookie_handler)
2454 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2455 GString *s = g_string_new ("");
2456 SoupURI * soup_uri = soup_message_get_uri(msg);
2457 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2458 run_handler(uzbl.behave.cookie_handler, s->str);
2460 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2461 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2462 if ( p != NULL ) *p = '\0';
2463 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2465 if (uzbl.comm.sync_stdout)
2466 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2468 g_string_free(s, TRUE);
2472 save_cookies (SoupMessage *msg, gpointer user_data){
2476 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2477 cookie = soup_cookie_to_set_cookie_header(ck->data);
2478 SoupURI * soup_uri = soup_message_get_uri(msg);
2479 GString *s = g_string_new ("");
2480 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2481 run_handler(uzbl.behave.cookie_handler, s->str);
2483 g_string_free(s, TRUE);
2488 /* --- WEBINSPECTOR --- */
2490 hide_window_cb(GtkWidget *widget, gpointer data) {
2493 gtk_widget_hide(widget);
2497 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2500 (void) web_inspector;
2501 GtkWidget* scrolled_window;
2502 GtkWidget* new_web_view;
2505 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2506 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2507 G_CALLBACK(hide_window_cb), NULL);
2509 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2510 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2511 gtk_widget_show(g->inspector_window);
2513 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2514 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2515 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2516 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2517 gtk_widget_show(scrolled_window);
2519 new_web_view = webkit_web_view_new();
2520 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2522 return WEBKIT_WEB_VIEW(new_web_view);
2526 inspector_show_window_cb (WebKitWebInspector* inspector){
2528 gtk_widget_show(uzbl.gui.inspector_window);
2532 /* TODO: Add variables and code to make use of these functions */
2534 inspector_close_window_cb (WebKitWebInspector* inspector){
2540 inspector_attach_window_cb (WebKitWebInspector* inspector){
2546 inspector_detach_window_cb (WebKitWebInspector* inspector){
2552 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2558 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2564 set_up_inspector() {
2566 WebKitWebSettings *settings = view_settings();
2567 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2569 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2570 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2571 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2573 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2574 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2575 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2577 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2581 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2583 uzbl_cmdprop *c = v;
2588 if(c->type == TYPE_STR)
2589 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2590 else if(c->type == TYPE_INT)
2591 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2592 else if(c->type == TYPE_FLOAT)
2593 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2597 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2601 printf("bind %s = %s %s\n", (char *)k ,
2602 (char *)a->name, a->param?(char *)a->param:"");
2607 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2608 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2612 retreive_geometry() {
2614 GString *buf = g_string_new("");
2616 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2617 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2619 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2621 if(uzbl.gui.geometry)
2622 g_free(uzbl.gui.geometry);
2623 uzbl.gui.geometry = g_string_free(buf, FALSE);
2626 /* set up gtk, gobject, variable defaults and other things that tests and other
2627 * external applications need to do anyhow */
2629 initialize(int argc, char *argv[]) {
2630 gtk_init (&argc, &argv);
2631 if (!g_thread_supported ())
2632 g_thread_init (NULL);
2633 uzbl.state.executable_path = g_strdup(argv[0]);
2634 uzbl.state.selected_url = NULL;
2635 uzbl.state.searchtx = NULL;
2637 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2638 g_option_context_add_main_entries (context, entries, NULL);
2639 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2640 g_option_context_parse (context, &argc, &argv, NULL);
2641 g_option_context_free(context);
2643 if (uzbl.behave.print_version) {
2644 printf("Commit: %s\n", COMMIT);
2648 /* initialize hash table */
2649 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2651 uzbl.net.soup_session = webkit_get_default_session();
2652 uzbl.state.keycmd = g_strdup("");
2654 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2655 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2656 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2657 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2658 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2659 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2661 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2662 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2663 uzbl.gui.sbar.progress_w = 10;
2665 /* HTML mode defaults*/
2666 uzbl.behave.html_buffer = g_string_new("");
2667 uzbl.behave.html_endmarker = g_strdup(".");
2668 uzbl.behave.html_timeout = 60;
2669 uzbl.behave.base_url = g_strdup("http://invalid");
2671 /* default mode indicators */
2672 uzbl.behave.insert_indicator = g_strdup("I");
2673 uzbl.behave.cmd_indicator = g_strdup("C");
2675 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2676 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2677 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2678 uzbl.info.arch = ARCH;
2679 uzbl.info.commit = COMMIT;
2682 make_var_to_name_hash();
2684 uzbl.gui.scrolled_win = create_browser();
2687 #ifndef UZBL_LIBRARY
2690 main (int argc, char* argv[]) {
2691 initialize(argc, argv);
2693 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2697 /* initial packing */
2698 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2699 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2701 if (uzbl.state.socket_id) {
2702 uzbl.gui.plug = create_plug ();
2703 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2704 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2706 uzbl.gui.main_window = create_window ();
2707 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2708 gtk_widget_show_all (uzbl.gui.main_window);
2709 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2712 if(!uzbl.state.instance_name)
2713 uzbl.state.instance_name = itos((int)uzbl.xwin);
2715 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2717 if (uzbl.state.verbose) {
2718 printf("Uzbl start location: %s\n", argv[0]);
2719 if (uzbl.state.socket_id)
2720 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2722 printf("window_id %i\n",(int) uzbl.xwin);
2723 printf("pid %i\n", getpid ());
2724 printf("name: %s\n", uzbl.state.instance_name);
2727 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2728 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2729 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2730 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2731 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2733 if(uzbl.gui.geometry)
2736 retreive_geometry();
2738 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2739 if (argc > 1 && !uzbl.state.uri)
2740 uri_override = g_strdup(argv[1]);
2741 gboolean verbose_override = uzbl.state.verbose;
2744 set_insert_mode(FALSE);
2746 if (!uzbl.behave.show_status)
2747 gtk_widget_hide(uzbl.gui.mainbar);
2754 if (verbose_override > uzbl.state.verbose)
2755 uzbl.state.verbose = verbose_override;
2758 set_var_value("uri", uri_override);
2759 g_free(uri_override);
2760 } else if (uzbl.state.uri)
2766 return EXIT_SUCCESS;
2770 /* vi: set et ts=4: */