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 }
108 const struct var_name_to_ptr_t {
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}}
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 struct var_name_to_ptr_t *n2v_p = &var_name_to_ptr;
218 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
220 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
225 /* --- UTILITY FUNCTIONS --- */
226 enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
228 get_exp_type(const gchar *s) {
232 else if(*(s+1) == '{')
233 return EXP_BRACED_VAR;
234 else if(*(s+1) == '<')
236 else if(*(s+1) == '[')
239 return EXP_SIMPLE_VAR;
246 * recurse == 1: don't expand '@(command)@'
247 * recurse == 2: don't expand '@<java script>@'
250 expand(const char *s, guint recurse) {
253 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
257 gchar *cmd_stdout = NULL;
259 GString *buf = g_string_new("");
260 GString *js_ret = g_string_new("");
265 g_string_append_c(buf, *++s);
270 etype = get_exp_type(s);
275 vend = strpbrk(s, end_simple_var);
276 if(!vend) vend = strchr(s, '\0');
280 vend = strchr(s, '}');
281 if(!vend) vend = strchr(s, '\0');
285 vend = strstr(s, ")@");
286 if(!vend) vend = strchr(s, '\0');
290 vend = strstr(s, ">@");
291 if(!vend) vend = strchr(s, '\0');
295 vend = strstr(s, "]@");
296 if(!vend) vend = strchr(s, '\0');
301 ret = g_strndup(s, vend-s);
303 if(etype == EXP_SIMPLE_VAR ||
304 etype == EXP_BRACED_VAR) {
305 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
306 if(c->type == TYPE_STR && *c->ptr.s != NULL) {
307 g_string_append(buf, (gchar *)*c->ptr.s);
308 } else if(c->type == TYPE_INT) {
309 g_string_append_printf(buf, "%d", *c->ptr.i);
311 else if(c->type == TYPE_FLOAT) {
312 g_string_append_printf(buf, "%f", *c->ptr.f);
316 if(etype == EXP_SIMPLE_VAR)
321 else if(recurse != 1 &&
323 mycmd = expand(ret, 1);
324 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
328 g_printerr("error on running command: %s\n", err->message);
331 else if (*cmd_stdout) {
332 size_t len = strlen(cmd_stdout);
334 if(len > 0 && cmd_stdout[len-1] == '\n')
335 cmd_stdout[--len] = '\0'; /* strip trailing newline */
337 g_string_append(buf, cmd_stdout);
342 else if(recurse != 2 &&
344 mycmd = expand(ret, 2);
345 eval_js(uzbl.gui.web_view, mycmd, js_ret);
349 g_string_append(buf, js_ret->str);
350 g_string_free(js_ret, TRUE);
351 js_ret = g_string_new("");
355 else if(etype == EXP_ESCAPE) {
356 mycmd = expand(ret, 0);
357 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
359 g_string_append(buf, escaped);
371 g_string_append_c(buf, *s);
376 g_string_free(js_ret, TRUE);
377 return g_string_free(buf, FALSE);
384 snprintf(tmp, sizeof(tmp), "%i", val);
385 return g_strdup(tmp);
389 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
392 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
395 str_replace (const char* search, const char* replace, const char* string) {
399 buf = g_strsplit (string, search, -1);
400 ret = g_strjoinv (replace, buf);
401 g_strfreev(buf); // somebody said this segfaults
407 read_file_by_line (const gchar *path) {
408 GIOChannel *chan = NULL;
409 gchar *readbuf = NULL;
411 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
414 chan = g_io_channel_new_file(path, "r", NULL);
417 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
418 const gchar* val = g_strdup (readbuf);
419 g_array_append_val (lines, val);
424 g_io_channel_unref (chan);
426 fprintf(stderr, "File '%s' not be read.\n", path);
433 parseenv (char* string) {
434 extern char** environ;
435 gchar* tmpstr = NULL;
439 while (environ[i] != NULL) {
440 gchar** env = g_strsplit (environ[i], "=", 2);
441 gchar* envname = g_strconcat ("$", env[0], NULL);
443 if (g_strrstr (string, envname) != NULL) {
444 tmpstr = g_strdup(string);
446 string = str_replace(envname, env[1], tmpstr);
451 g_strfreev (env); // somebody said this breaks uzbl
459 setup_signal(int signr, sigfunc *shandler) {
460 struct sigaction nh, oh;
462 nh.sa_handler = shandler;
463 sigemptyset(&nh.sa_mask);
466 if(sigaction(signr, &nh, &oh) < 0)
474 if (uzbl.behave.fifo_dir)
475 unlink (uzbl.comm.fifo_path);
476 if (uzbl.behave.socket_dir)
477 unlink (uzbl.comm.socket_path);
479 g_free(uzbl.state.executable_path);
480 g_free(uzbl.state.keycmd);
481 g_hash_table_destroy(uzbl.bindings);
482 g_hash_table_destroy(uzbl.behave.commands);
485 /* used for html_mode_timeout
486 * be sure to extend this function to use
487 * more timers if needed in other places
490 set_timeout(int seconds) {
492 memset(&t, 0, sizeof t);
494 t.it_value.tv_sec = seconds;
495 t.it_value.tv_usec = 0;
496 setitimer(ITIMER_REAL, &t, NULL);
499 /* --- SIGNAL HANDLER --- */
502 catch_sigterm(int s) {
508 catch_sigint(int s) {
518 set_var_value("mode", "0");
523 /* --- CALLBACKS --- */
526 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
529 (void) navigation_action;
530 (void) policy_decision;
532 const gchar* uri = webkit_network_request_get_uri (request);
533 if (uzbl.state.verbose)
534 printf("New window requested -> %s \n", uri);
535 webkit_web_policy_decision_use(policy_decision);
540 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
545 /* If we can display it, let's display it... */
546 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
547 webkit_web_policy_decision_use (policy_decision);
551 /* ...everything we can't displayed is downloaded */
552 webkit_web_policy_decision_download (policy_decision);
556 /*@null@*/ WebKitWebView*
557 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
561 if (uzbl.state.selected_url != NULL) {
562 if (uzbl.state.verbose)
563 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
564 new_window_load_uri(uzbl.state.selected_url);
566 if (uzbl.state.verbose)
567 printf("New web view -> %s\n","Nothing to open, exiting");
573 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
576 if (uzbl.behave.download_handler) {
577 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
578 if (uzbl.state.verbose)
579 printf("Download -> %s\n",uri);
580 /* if urls not escaped, we may have to escape and quote uri before this call */
581 run_handler(uzbl.behave.download_handler, uri);
586 /* scroll a bar in a given direction */
588 scroll (GtkAdjustment* bar, GArray *argv) {
592 gdouble page_size = gtk_adjustment_get_page_size(bar);
593 gdouble value = gtk_adjustment_get_value(bar);
594 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
597 value += page_size * amount * 0.01;
601 max_value = gtk_adjustment_get_upper(bar) - page_size;
603 if (value > max_value)
604 value = max_value; /* don't scroll past the end of the page */
606 gtk_adjustment_set_value (bar, value);
610 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
611 (void) page; (void) argv; (void) result;
612 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
616 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) argv; (void) result;
618 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
619 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
623 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
624 (void) page; (void) result;
625 scroll(uzbl.gui.bar_v, argv);
629 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
630 (void) page; (void) result;
631 scroll(uzbl.gui.bar_h, argv);
636 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
637 if(uzbl.state.verbose)
638 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
640 /* update geometry var with the actual geometry
641 this is necessary as some WMs don't seem to honour
642 the above setting and we don't want to end up with
643 wrong geometry information
650 if (!uzbl.behave.show_status) {
651 gtk_widget_hide(uzbl.gui.mainbar);
653 gtk_widget_show(uzbl.gui.mainbar);
659 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
664 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
668 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
673 if (uzbl.behave.show_status) {
674 gtk_widget_hide(uzbl.gui.mainbar);
676 gtk_widget_show(uzbl.gui.mainbar);
678 uzbl.behave.show_status = !uzbl.behave.show_status;
683 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
687 //Set selected_url state variable
688 g_free(uzbl.state.selected_url);
689 uzbl.state.selected_url = NULL;
691 uzbl.state.selected_url = g_strdup(link);
697 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
700 const gchar *title = webkit_web_view_get_title(web_view);
701 if (uzbl.gui.main_title)
702 g_free (uzbl.gui.main_title);
703 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
708 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
711 uzbl.gui.sbar.load_progress = progress;
713 g_free(uzbl.gui.sbar.progress_bar);
714 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
720 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
724 if (uzbl.behave.load_finish_handler)
725 run_handler(uzbl.behave.load_finish_handler, "");
728 void clear_keycmd() {
729 g_free(uzbl.state.keycmd);
730 uzbl.state.keycmd = g_strdup("");
734 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
738 uzbl.gui.sbar.load_progress = 0;
739 clear_keycmd(); // don't need old commands to remain on new page?
740 if (uzbl.behave.load_start_handler)
741 run_handler(uzbl.behave.load_start_handler, "");
745 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
748 g_free (uzbl.state.uri);
749 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
750 uzbl.state.uri = g_string_free (newuri, FALSE);
751 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
752 set_insert_mode(uzbl.behave.always_insert_mode);
755 if (uzbl.behave.load_commit_handler)
756 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
760 destroy_cb (GtkWidget* widget, gpointer data) {
768 if (uzbl.behave.history_handler) {
770 struct tm * timeinfo;
773 timeinfo = localtime ( &rawtime );
774 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
775 run_handler(uzbl.behave.history_handler, date);
780 /* VIEW funcs (little webkit wrappers) */
781 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
783 VIEWFUNC(reload_bypass_cache)
784 VIEWFUNC(stop_loading)
791 /* -- command to callback/function map for things we cannot attach to any signals */
792 struct {const char *key; CommandInfo value;} cmdlist[] =
793 { /* key function no_split */
794 { "back", {view_go_back, 0} },
795 { "forward", {view_go_forward, 0} },
796 { "scroll_vert", {scroll_vert, 0} },
797 { "scroll_horz", {scroll_horz, 0} },
798 { "scroll_begin", {scroll_begin, 0} },
799 { "scroll_end", {scroll_end, 0} },
800 { "reload", {view_reload, 0}, },
801 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
802 { "stop", {view_stop_loading, 0}, },
803 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
804 { "zoom_out", {view_zoom_out, 0}, },
805 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
806 { "uri", {load_uri, TRUE} },
807 { "js", {run_js, TRUE} },
808 { "script", {run_external_js, 0} },
809 { "toggle_status", {toggle_status_cb, 0} },
810 { "spawn", {spawn, 0} },
811 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
812 { "sh", {spawn_sh, 0} },
813 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
814 { "exit", {close_uzbl, 0} },
815 { "search", {search_forward_text, TRUE} },
816 { "search_reverse", {search_reverse_text, TRUE} },
817 { "dehilight", {dehilight, 0} },
818 { "toggle_insert_mode", {toggle_insert_mode, 0} },
819 { "set", {set_var, TRUE} },
820 //{ "get", {get_var, TRUE} },
821 { "bind", {act_bind, TRUE} },
822 { "dump_config", {act_dump_config, 0} },
823 { "keycmd", {keycmd, TRUE} },
824 { "keycmd_nl", {keycmd_nl, TRUE} },
825 { "keycmd_bs", {keycmd_bs, 0} },
826 { "chain", {chain, 0} },
827 { "print", {print, TRUE} }
834 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
836 for (i = 0; i < LENGTH(cmdlist); i++)
837 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
840 /* -- CORE FUNCTIONS -- */
843 free_action(gpointer act) {
844 Action *action = (Action*)act;
845 g_free(action->name);
847 g_free(action->param);
852 new_action(const gchar *name, const gchar *param) {
853 Action *action = g_new(Action, 1);
855 action->name = g_strdup(name);
857 action->param = g_strdup(param);
859 action->param = NULL;
865 file_exists (const char * filename) {
866 return (access(filename, F_OK) == 0);
870 set_var(WebKitWebView *page, GArray *argv, GString *result) {
871 (void) page; (void) result;
872 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
873 if (split[0] != NULL) {
874 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
875 set_var_value(g_strstrip(split[0]), value);
882 print(WebKitWebView *page, GArray *argv, GString *result) {
883 (void) page; (void) result;
886 buf = expand(argv_idx(argv, 0), 0);
887 g_string_assign(result, buf);
892 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
893 (void) page; (void) result;
894 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
895 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
896 add_binding(g_strstrip(split[0]), value);
914 set_mode_indicator() {
915 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
916 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
921 set_mode_indicator();
926 set_insert_mode(gboolean mode) {
927 uzbl.behave.insert_mode = mode;
928 set_mode_indicator();
932 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
933 (void) page; (void) result;
935 if (argv_idx(argv, 0)) {
936 if (strcmp (argv_idx(argv, 0), "0") == 0) {
937 set_insert_mode(FALSE);
939 set_insert_mode(TRUE);
942 set_insert_mode( !uzbl.behave.insert_mode );
949 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
952 if (argv_idx(argv, 0)) {
953 GString* newuri = g_string_new (argv_idx(argv, 0));
954 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
955 run_js(web_view, argv, NULL);
958 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
959 g_string_prepend (newuri, "http://");
960 /* if we do handle cookies, ask our handler for them */
961 webkit_web_view_load_uri (web_view, newuri->str);
962 g_string_free (newuri, TRUE);
969 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
970 size_t argumentCount, const JSValueRef arguments[],
971 JSValueRef* exception) {
976 JSStringRef js_result_string;
977 GString *result = g_string_new("");
979 if (argumentCount >= 1) {
980 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
981 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
982 char ctl_line[arg_size];
983 JSStringGetUTF8CString(arg, ctl_line, arg_size);
985 parse_cmd_line(ctl_line, result);
987 JSStringRelease(arg);
989 js_result_string = JSStringCreateWithUTF8CString(result->str);
991 g_string_free(result, TRUE);
993 return JSValueMakeString(ctx, js_result_string);
996 JSStaticFunction js_static_functions[] = {
997 {"run", js_run_command, kJSPropertyAttributeNone},
1002 /* This function creates the class and its definition, only once */
1003 if (!uzbl.js.initialized) {
1004 /* it would be pretty cool to make this dynamic */
1005 uzbl.js.classdef = kJSClassDefinitionEmpty;
1006 uzbl.js.classdef.staticFunctions = js_static_functions;
1008 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1014 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1015 WebKitWebFrame *frame;
1016 JSGlobalContextRef context;
1017 JSObjectRef globalobject;
1018 JSStringRef var_name;
1020 JSStringRef js_script;
1021 JSValueRef js_result;
1022 JSStringRef js_result_string;
1023 size_t js_result_size;
1027 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1028 context = webkit_web_frame_get_global_context(frame);
1029 globalobject = JSContextGetGlobalObject(context);
1031 /* uzbl javascript namespace */
1032 var_name = JSStringCreateWithUTF8CString("Uzbl");
1033 JSObjectSetProperty(context, globalobject, var_name,
1034 JSObjectMake(context, uzbl.js.classref, NULL),
1035 kJSClassAttributeNone, NULL);
1037 /* evaluate the script and get return value*/
1038 js_script = JSStringCreateWithUTF8CString(script);
1039 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1040 if (js_result && !JSValueIsUndefined(context, js_result)) {
1041 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1042 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1044 if (js_result_size) {
1045 char js_result_utf8[js_result_size];
1046 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1047 g_string_assign(result, js_result_utf8);
1050 JSStringRelease(js_result_string);
1054 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1056 JSStringRelease(var_name);
1057 JSStringRelease(js_script);
1061 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1062 if (argv_idx(argv, 0))
1063 eval_js(web_view, argv_idx(argv, 0), result);
1067 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1069 if (argv_idx(argv, 0)) {
1070 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1075 while ((line = g_array_index(lines, gchar*, i))) {
1077 js = g_strdup (line);
1079 gchar* newjs = g_strconcat (js, line, NULL);
1086 if (uzbl.state.verbose)
1087 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1089 if (argv_idx (argv, 1)) {
1090 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1094 eval_js (web_view, js, result);
1096 g_array_free (lines, TRUE);
1101 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1102 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1103 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1104 webkit_web_view_unmark_text_matches (page);
1105 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1106 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1110 if (uzbl.state.searchtx) {
1111 if (uzbl.state.verbose)
1112 printf ("Searching: %s\n", uzbl.state.searchtx);
1113 webkit_web_view_set_highlight_text_matches (page, TRUE);
1114 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1119 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1121 search_text(page, argv, TRUE);
1125 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1127 search_text(page, argv, FALSE);
1131 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1132 (void) argv; (void) result;
1133 webkit_web_view_set_highlight_text_matches (page, FALSE);
1138 new_window_load_uri (const gchar * uri) {
1139 if (uzbl.behave.new_window) {
1140 GString *s = g_string_new ("");
1141 g_string_printf(s, "'%s'", uri);
1142 run_handler(uzbl.behave.new_window, s->str);
1145 GString* to_execute = g_string_new ("");
1146 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1148 for (i = 0; entries[i].long_name != NULL; i++) {
1149 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1150 gchar** str = (gchar**)entries[i].arg_data;
1152 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1156 if (uzbl.state.verbose)
1157 printf("\n%s\n", to_execute->str);
1158 g_spawn_command_line_async (to_execute->str, NULL);
1159 g_string_free (to_execute, TRUE);
1163 chain (WebKitWebView *page, GArray *argv, GString *result) {
1164 (void) page; (void) result;
1166 gchar **parts = NULL;
1168 while ((a = argv_idx(argv, i++))) {
1169 parts = g_strsplit (a, " ", 2);
1171 parse_command(parts[0], parts[1], result);
1177 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1181 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1187 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1191 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1197 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1202 int len = strlen(uzbl.state.keycmd);
1203 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1205 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1210 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1217 /* --Statusbar functions-- */
1219 build_progressbar_ascii(int percent) {
1220 int width=uzbl.gui.sbar.progress_w;
1223 GString *bar = g_string_new("");
1225 l = (double)percent*((double)width/100.);
1226 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1228 for(i=0; i<(int)l; i++)
1229 g_string_append(bar, uzbl.gui.sbar.progress_s);
1232 g_string_append(bar, uzbl.gui.sbar.progress_u);
1234 return g_string_free(bar, FALSE);
1236 /* --End Statusbar functions-- */
1239 sharg_append(GArray *a, const gchar *str) {
1240 const gchar *s = (str ? str : "");
1241 g_array_append_val(a, s);
1244 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1246 run_command (const gchar *command, const guint npre, const gchar **args,
1247 const gboolean sync, char **output_stdout) {
1248 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1251 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1252 gchar *pid = itos(getpid());
1253 gchar *xwin = itos(uzbl.xwin);
1255 sharg_append(a, command);
1256 for (i = 0; i < npre; i++) /* add n args before the default vars */
1257 sharg_append(a, args[i]);
1258 sharg_append(a, uzbl.state.config_file);
1259 sharg_append(a, pid);
1260 sharg_append(a, xwin);
1261 sharg_append(a, uzbl.comm.fifo_path);
1262 sharg_append(a, uzbl.comm.socket_path);
1263 sharg_append(a, uzbl.state.uri);
1264 sharg_append(a, uzbl.gui.main_title);
1266 for (i = npre; i < g_strv_length((gchar**)args); i++)
1267 sharg_append(a, args[i]);
1271 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1273 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1274 NULL, NULL, output_stdout, NULL, NULL, &err);
1275 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1276 NULL, NULL, NULL, &err);
1278 if (uzbl.state.verbose) {
1279 GString *s = g_string_new("spawned:");
1280 for (i = 0; i < (a->len); i++) {
1281 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1282 g_string_append_printf(s, " %s", qarg);
1285 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1286 printf("%s\n", s->str);
1287 g_string_free(s, TRUE);
1289 printf("Stdout: %s\n", *output_stdout);
1293 g_printerr("error on run_command: %s\n", err->message);
1298 g_array_free (a, TRUE);
1303 split_quoted(const gchar* src, const gboolean unquote) {
1304 /* split on unquoted space, return array of strings;
1305 remove a layer of quotes and backslashes if unquote */
1306 if (!src) return NULL;
1308 gboolean dq = FALSE;
1309 gboolean sq = FALSE;
1310 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311 GString *s = g_string_new ("");
1315 for (p = src; *p != '\0'; p++) {
1316 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1317 else if (*p == '\\') { g_string_append_c(s, *p++);
1318 g_string_append_c(s, *p); }
1319 else if ((*p == '"') && unquote && !sq) dq = !dq;
1320 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1322 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1323 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1325 else if ((*p == ' ') && !dq && !sq) {
1326 dup = g_strdup(s->str);
1327 g_array_append_val(a, dup);
1328 g_string_truncate(s, 0);
1329 } else g_string_append_c(s, *p);
1331 dup = g_strdup(s->str);
1332 g_array_append_val(a, dup);
1333 ret = (gchar**)a->data;
1334 g_array_free (a, FALSE);
1335 g_string_free (s, TRUE);
1340 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1341 (void)web_view; (void)result;
1342 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1343 if (argv_idx(argv, 0))
1344 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1348 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1349 (void)web_view; (void)result;
1351 if (argv_idx(argv, 0))
1352 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1353 TRUE, &uzbl.comm.sync_stdout);
1357 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1358 (void)web_view; (void)result;
1359 if (!uzbl.behave.shell_cmd) {
1360 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1365 gchar *spacer = g_strdup("");
1366 g_array_insert_val(argv, 1, spacer);
1367 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1369 for (i = 1; i < g_strv_length(cmd); i++)
1370 g_array_prepend_val(argv, cmd[i]);
1372 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1378 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1379 (void)web_view; (void)result;
1380 if (!uzbl.behave.shell_cmd) {
1381 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1386 gchar *spacer = g_strdup("");
1387 g_array_insert_val(argv, 1, spacer);
1388 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1390 for (i = 1; i < g_strv_length(cmd); i++)
1391 g_array_prepend_val(argv, cmd[i]);
1393 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1394 TRUE, &uzbl.comm.sync_stdout);
1400 parse_command(const char *cmd, const char *param, GString *result) {
1403 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1405 gchar **par = split_quoted(param, TRUE);
1406 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1408 if (c->no_split) { /* don't split */
1409 sharg_append(a, param);
1411 for (i = 0; i < g_strv_length(par); i++)
1412 sharg_append(a, par[i]);
1415 if (result == NULL) {
1416 GString *result_print = g_string_new("");
1418 c->function(uzbl.gui.web_view, a, result_print);
1419 if (result_print->len)
1420 printf("%*s\n", (int)result_print->len, result_print->str);
1422 g_string_free(result_print, TRUE);
1424 c->function(uzbl.gui.web_view, a, result);
1427 g_array_free (a, TRUE);
1430 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1437 if(uzbl.net.proxy_url == NULL || *uzbl.net.proxy_url == ' ') {
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(const 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, const 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;
2408 g_free(temporary_file);
2414 State *s = &uzbl.state;
2415 Network *n = &uzbl.net;
2417 for (i = 0; default_config[i].command != NULL; i++) {
2418 parse_cmd_line(default_config[i].command, NULL);
2421 if (g_strcmp0(s->config_file, "-") == 0) {
2422 s->config_file = NULL;
2426 else if (!s->config_file) {
2427 s->config_file = find_xdg_file (0, "/uzbl/config");
2430 if (s->config_file) {
2431 GArray* lines = read_file_by_line (s->config_file);
2435 while ((line = g_array_index(lines, gchar*, i))) {
2436 parse_cmd_line (line, NULL);
2440 g_array_free (lines, TRUE);
2442 if (uzbl.state.verbose)
2443 printf ("No configuration file loaded.\n");
2446 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2449 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2452 if (!uzbl.behave.cookie_handler)
2455 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2456 GString *s = g_string_new ("");
2457 SoupURI * soup_uri = soup_message_get_uri(msg);
2458 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2459 run_handler(uzbl.behave.cookie_handler, s->str);
2461 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2462 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2463 if ( p != NULL ) *p = '\0';
2464 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2466 if (uzbl.comm.sync_stdout)
2467 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2469 g_string_free(s, TRUE);
2473 save_cookies (SoupMessage *msg, gpointer user_data){
2477 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2478 cookie = soup_cookie_to_set_cookie_header(ck->data);
2479 SoupURI * soup_uri = soup_message_get_uri(msg);
2480 GString *s = g_string_new ("");
2481 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2482 run_handler(uzbl.behave.cookie_handler, s->str);
2484 g_string_free(s, TRUE);
2489 /* --- WEBINSPECTOR --- */
2491 hide_window_cb(GtkWidget *widget, gpointer data) {
2494 gtk_widget_hide(widget);
2498 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2501 (void) web_inspector;
2502 GtkWidget* scrolled_window;
2503 GtkWidget* new_web_view;
2506 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2507 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2508 G_CALLBACK(hide_window_cb), NULL);
2510 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2511 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2512 gtk_widget_show(g->inspector_window);
2514 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2515 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2516 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2517 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2518 gtk_widget_show(scrolled_window);
2520 new_web_view = webkit_web_view_new();
2521 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2523 return WEBKIT_WEB_VIEW(new_web_view);
2527 inspector_show_window_cb (WebKitWebInspector* inspector){
2529 gtk_widget_show(uzbl.gui.inspector_window);
2533 /* TODO: Add variables and code to make use of these functions */
2535 inspector_close_window_cb (WebKitWebInspector* inspector){
2541 inspector_attach_window_cb (WebKitWebInspector* inspector){
2547 inspector_detach_window_cb (WebKitWebInspector* inspector){
2553 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2559 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2565 set_up_inspector() {
2567 WebKitWebSettings *settings = view_settings();
2568 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2570 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2571 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2573 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2574 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2575 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2576 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2578 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2582 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2584 uzbl_cmdprop *c = v;
2589 if(c->type == TYPE_STR)
2590 printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " ");
2591 else if(c->type == TYPE_INT)
2592 printf("set %s = %d\n", (char *)k, *c->ptr.i);
2593 else if(c->type == TYPE_FLOAT)
2594 printf("set %s = %f\n", (char *)k, *c->ptr.f);
2598 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2602 printf("bind %s = %s %s\n", (char *)k ,
2603 (char *)a->name, a->param?(char *)a->param:"");
2608 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2609 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2613 retreive_geometry() {
2615 GString *buf = g_string_new("");
2617 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2618 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2620 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2622 if(uzbl.gui.geometry)
2623 g_free(uzbl.gui.geometry);
2624 uzbl.gui.geometry = g_string_free(buf, FALSE);
2627 /* set up gtk, gobject, variable defaults and other things that tests and other
2628 * external applications need to do anyhow */
2630 initialize(int argc, char *argv[]) {
2631 gtk_init (&argc, &argv);
2632 if (!g_thread_supported ())
2633 g_thread_init (NULL);
2634 uzbl.state.executable_path = g_strdup(argv[0]);
2635 uzbl.state.selected_url = NULL;
2636 uzbl.state.searchtx = NULL;
2638 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2639 g_option_context_add_main_entries (context, entries, NULL);
2640 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2641 g_option_context_parse (context, &argc, &argv, NULL);
2642 g_option_context_free(context);
2644 if (uzbl.behave.print_version) {
2645 printf("Commit: %s\n", COMMIT);
2649 /* initialize hash table */
2650 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2652 uzbl.net.soup_session = webkit_get_default_session();
2653 uzbl.state.keycmd = g_strdup("");
2655 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2656 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2657 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2658 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2659 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2660 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2662 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2663 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2664 uzbl.gui.sbar.progress_w = 10;
2666 /* HTML mode defaults*/
2667 uzbl.behave.html_buffer = g_string_new("");
2668 uzbl.behave.html_endmarker = g_strdup(".");
2669 uzbl.behave.html_timeout = 60;
2670 uzbl.behave.base_url = g_strdup("http://invalid");
2672 /* default mode indicators */
2673 uzbl.behave.insert_indicator = g_strdup("I");
2674 uzbl.behave.cmd_indicator = g_strdup("C");
2676 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2677 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2678 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2679 uzbl.info.arch = ARCH;
2680 uzbl.info.commit = COMMIT;
2683 make_var_to_name_hash();
2685 uzbl.gui.scrolled_win = create_browser();
2688 #ifndef UZBL_LIBRARY
2691 main (int argc, char* argv[]) {
2692 initialize(argc, argv);
2694 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2698 /* initial packing */
2699 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2700 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2702 if (uzbl.state.socket_id) {
2703 uzbl.gui.plug = create_plug ();
2704 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2705 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2707 uzbl.gui.main_window = create_window ();
2708 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2709 gtk_widget_show_all (uzbl.gui.main_window);
2710 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2713 if(!uzbl.state.instance_name)
2714 uzbl.state.instance_name = itos((int)uzbl.xwin);
2716 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2718 if (uzbl.state.verbose) {
2719 printf("Uzbl start location: %s\n", argv[0]);
2720 if (uzbl.state.socket_id)
2721 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2723 printf("window_id %i\n",(int) uzbl.xwin);
2724 printf("pid %i\n", getpid ());
2725 printf("name: %s\n", uzbl.state.instance_name);
2728 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2729 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2730 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2731 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2732 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2734 if(uzbl.gui.geometry)
2737 retreive_geometry();
2739 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2740 if (argc > 1 && !uzbl.state.uri)
2741 uri_override = g_strdup(argv[1]);
2742 gboolean verbose_override = uzbl.state.verbose;
2745 set_insert_mode(FALSE);
2747 if (!uzbl.behave.show_status)
2748 gtk_widget_hide(uzbl.gui.mainbar);
2755 if (verbose_override > uzbl.state.verbose)
2756 uzbl.state.verbose = verbose_override;
2759 set_var_value("uri", uri_override);
2760 g_free(uri_override);
2761 } else if (uzbl.state.uri)
2767 return EXIT_SUCCESS;
2771 /* vi: set et ts=4: */