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_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 (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);
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 {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 == ' '
1438 || uzbl.net.proxy_url == NULL) {
1439 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1440 (GType) SOUP_SESSION_PROXY_URI);
1443 suri = soup_uri_new(uzbl.net.proxy_url);
1444 g_object_set(G_OBJECT(uzbl.net.soup_session),
1445 SOUP_SESSION_PROXY_URI,
1447 soup_uri_free(suri);
1454 if(file_exists(uzbl.gui.icon)) {
1455 if (uzbl.gui.main_window)
1456 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1458 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1464 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1465 g_array_append_val (a, uzbl.state.uri);
1466 load_uri(uzbl.gui.web_view, a, NULL);
1467 g_array_free (a, TRUE);
1471 cmd_always_insert_mode() {
1472 set_insert_mode(uzbl.behave.always_insert_mode);
1478 g_object_set(G_OBJECT(uzbl.net.soup_session),
1479 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1483 cmd_max_conns_host() {
1484 g_object_set(G_OBJECT(uzbl.net.soup_session),
1485 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1490 soup_session_remove_feature
1491 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1492 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1493 /*g_free(uzbl.net.soup_logger);*/
1495 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1496 soup_session_add_feature(uzbl.net.soup_session,
1497 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1502 return webkit_web_view_get_settings(uzbl.gui.web_view);
1507 WebKitWebSettings *ws = view_settings();
1508 if (uzbl.behave.font_size > 0) {
1509 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1512 if (uzbl.behave.monospace_size > 0) {
1513 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1514 uzbl.behave.monospace_size, NULL);
1516 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1517 uzbl.behave.font_size, NULL);
1523 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1527 cmd_disable_plugins() {
1528 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1529 !uzbl.behave.disable_plugins, NULL);
1533 cmd_disable_scripts() {
1534 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1535 !uzbl.behave.disable_scripts, NULL);
1539 cmd_minimum_font_size() {
1540 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1541 uzbl.behave.minimum_font_size, NULL);
1544 cmd_autoload_img() {
1545 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1546 uzbl.behave.autoload_img, NULL);
1551 cmd_autoshrink_img() {
1552 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1553 uzbl.behave.autoshrink_img, NULL);
1558 cmd_enable_spellcheck() {
1559 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1560 uzbl.behave.enable_spellcheck, NULL);
1564 cmd_enable_private() {
1565 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1566 uzbl.behave.enable_private, NULL);
1571 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1572 uzbl.behave.print_bg, NULL);
1577 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1578 uzbl.behave.style_uri, NULL);
1582 cmd_resizable_txt() {
1583 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1584 uzbl.behave.resizable_txt, NULL);
1588 cmd_default_encoding() {
1589 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1590 uzbl.behave.default_encoding, NULL);
1594 cmd_enforce_96dpi() {
1595 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1596 uzbl.behave.enforce_96dpi, NULL);
1600 cmd_caret_browsing() {
1601 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1602 uzbl.behave.caret_browsing, NULL);
1606 cmd_cookie_handler() {
1607 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1608 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1609 if ((g_strcmp0(split[0], "sh") == 0) ||
1610 (g_strcmp0(split[0], "spawn") == 0)) {
1611 g_free (uzbl.behave.cookie_handler);
1612 uzbl.behave.cookie_handler =
1613 g_strdup_printf("sync_%s %s", split[0], split[1]);
1620 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1621 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1622 if ((g_strcmp0(split[0], "sh") == 0) ||
1623 (g_strcmp0(split[0], "spawn") == 0)) {
1624 g_free (uzbl.behave.new_window);
1625 uzbl.behave.new_window =
1626 g_strdup_printf("%s %s", split[0], split[1]);
1633 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1638 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1643 if(uzbl.behave.inject_html) {
1644 webkit_web_view_load_html_string (uzbl.gui.web_view,
1645 uzbl.behave.inject_html, NULL);
1654 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1655 uzbl.behave.modmask = 0;
1657 if(uzbl.behave.modkey)
1658 g_free(uzbl.behave.modkey);
1659 uzbl.behave.modkey = buf;
1661 for (i = 0; modkeys[i].key != NULL; i++) {
1662 if (g_strrstr(buf, modkeys[i].key))
1663 uzbl.behave.modmask |= modkeys[i].mask;
1669 if (*uzbl.net.useragent == ' ') {
1670 g_free (uzbl.net.useragent);
1671 uzbl.net.useragent = NULL;
1673 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1674 uzbl.net.useragent, NULL);
1680 gtk_widget_ref(uzbl.gui.scrolled_win);
1681 gtk_widget_ref(uzbl.gui.mainbar);
1682 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1683 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1685 if(uzbl.behave.status_top) {
1686 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1687 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.scrolled_win, TRUE, TRUE, 0);
1691 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1693 gtk_widget_unref(uzbl.gui.scrolled_win);
1694 gtk_widget_unref(uzbl.gui.mainbar);
1695 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1700 set_var_value(gchar *name, gchar *val) {
1701 uzbl_cmdprop *c = NULL;
1705 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1706 if(!c->writeable) return FALSE;
1708 /* check for the variable type */
1709 if (c->type == TYPE_STR) {
1710 buf = expand(val, 0);
1713 } else if(c->type == TYPE_INT) {
1714 buf = expand(val, 0);
1715 *c->ptr.i = (int)strtoul(buf, &endp, 10);
1717 } else if (c->type == TYPE_FLOAT) {
1718 buf = expand(val, 0);
1719 *c->ptr.f = strtod(buf, &endp);
1723 /* invoke a command specific function */
1724 if(c->func) c->func();
1731 Behaviour *b = &uzbl.behave;
1733 if(b->html_buffer->str) {
1734 webkit_web_view_load_html_string (uzbl.gui.web_view,
1735 b->html_buffer->str, b->base_url);
1736 g_string_free(b->html_buffer, TRUE);
1737 b->html_buffer = g_string_new("");
1741 enum {M_CMD, M_HTML};
1743 parse_cmd_line(const char *ctl_line, GString *result) {
1744 Behaviour *b = &uzbl.behave;
1747 if(b->mode == M_HTML) {
1748 len = strlen(b->html_endmarker);
1749 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1750 if(len == strlen(ctl_line)-1 &&
1751 !strncmp(b->html_endmarker, ctl_line, len)) {
1753 set_var_value("mode", "0");
1758 set_timeout(b->html_timeout);
1759 g_string_append(b->html_buffer, ctl_line);
1762 else if((ctl_line[0] == '#') /* Comments */
1763 || (ctl_line[0] == ' ')
1764 || (ctl_line[0] == '\n'))
1765 ; /* ignore these lines */
1766 else { /* parse a command */
1768 gchar **tokens = NULL;
1769 len = strlen(ctl_line);
1771 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1772 ctlstrip = g_strndup(ctl_line, len - 1);
1773 else ctlstrip = g_strdup(ctl_line);
1775 tokens = g_strsplit(ctlstrip, " ", 2);
1776 parse_command(tokens[0], tokens[1], result);
1783 build_stream_name(int type, const gchar* dir) {
1784 State *s = &uzbl.state;
1788 str = g_strdup_printf
1789 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1790 } else if (type == SOCKET) {
1791 str = g_strdup_printf
1792 ("%s/uzbl_socket_%s", dir, s->instance_name);
1798 control_fifo(GIOChannel *gio, GIOCondition condition) {
1799 if (uzbl.state.verbose)
1800 printf("triggered\n");
1805 if (condition & G_IO_HUP)
1806 g_error ("Fifo: Read end of pipe died!\n");
1809 g_error ("Fifo: GIOChannel broke\n");
1811 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1812 if (ret == G_IO_STATUS_ERROR) {
1813 g_error ("Fifo: Error reading: %s\n", err->message);
1817 parse_cmd_line(ctl_line, NULL);
1824 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1825 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1826 if (unlink(uzbl.comm.fifo_path) == -1)
1827 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1828 g_free(uzbl.comm.fifo_path);
1829 uzbl.comm.fifo_path = NULL;
1832 GIOChannel *chan = NULL;
1833 GError *error = NULL;
1834 gchar *path = build_stream_name(FIFO, dir);
1836 if (!file_exists(path)) {
1837 if (mkfifo (path, 0666) == 0) {
1838 // 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.
1839 chan = g_io_channel_new_file(path, "r+", &error);
1841 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1842 if (uzbl.state.verbose)
1843 printf ("init_fifo: created successfully as %s\n", path);
1844 uzbl.comm.fifo_path = path;
1846 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1847 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1848 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1849 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1851 /* if we got this far, there was an error; cleanup */
1852 if (error) g_error_free (error);
1859 control_stdin(GIOChannel *gio, GIOCondition condition) {
1861 gchar *ctl_line = NULL;
1864 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1865 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1868 parse_cmd_line(ctl_line, NULL);
1876 GIOChannel *chan = NULL;
1877 GError *error = NULL;
1879 chan = g_io_channel_unix_new(fileno(stdin));
1881 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1882 g_error ("Stdin: could not add watch\n");
1884 if (uzbl.state.verbose)
1885 printf ("Stdin: watch added successfully\n");
1888 g_error ("Stdin: Error while opening: %s\n", error->message);
1890 if (error) g_error_free (error);
1894 control_socket(GIOChannel *chan) {
1895 struct sockaddr_un remote;
1896 unsigned int t = sizeof(remote);
1898 GIOChannel *clientchan;
1900 clientsock = accept (g_io_channel_unix_get_fd(chan),
1901 (struct sockaddr *) &remote, &t);
1903 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1904 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1905 (GIOFunc) control_client_socket, clientchan);
1912 control_client_socket(GIOChannel *clientchan) {
1914 GString *result = g_string_new("");
1915 GError *error = NULL;
1919 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1920 if (ret == G_IO_STATUS_ERROR) {
1921 g_warning ("Error reading: %s\n", error->message);
1922 g_io_channel_shutdown(clientchan, TRUE, &error);
1924 } else if (ret == G_IO_STATUS_EOF) {
1925 /* shutdown and remove channel watch from main loop */
1926 g_io_channel_shutdown(clientchan, TRUE, &error);
1931 parse_cmd_line (ctl_line, result);
1932 g_string_append_c(result, '\n');
1933 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1935 if (ret == G_IO_STATUS_ERROR) {
1936 g_warning ("Error writing: %s", error->message);
1938 g_io_channel_flush(clientchan, &error);
1941 if (error) g_error_free (error);
1942 g_string_free(result, TRUE);
1948 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1949 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1950 if (unlink(uzbl.comm.socket_path) == -1)
1951 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1952 g_free(uzbl.comm.socket_path);
1953 uzbl.comm.socket_path = NULL;
1961 GIOChannel *chan = NULL;
1963 struct sockaddr_un local;
1964 gchar *path = build_stream_name(SOCKET, dir);
1966 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1968 local.sun_family = AF_UNIX;
1969 strcpy (local.sun_path, path);
1970 unlink (local.sun_path);
1972 len = strlen (local.sun_path) + sizeof (local.sun_family);
1973 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1974 if (uzbl.state.verbose)
1975 printf ("init_socket: opened in %s\n", path);
1978 if( (chan = g_io_channel_unix_new(sock)) ) {
1979 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1980 uzbl.comm.socket_path = path;
1983 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1985 /* if we got this far, there was an error; cleanup */
1992 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1993 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1995 // this function may be called very early when the templates are not set (yet), hence the checks
1997 update_title (void) {
1998 Behaviour *b = &uzbl.behave;
2001 if (b->show_status) {
2002 if (b->title_format_short) {
2003 parsed = expand(b->title_format_short, 0);
2004 if (uzbl.gui.main_window)
2005 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2008 if (b->status_format) {
2009 parsed = expand(b->status_format, 0);
2010 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2013 if (b->status_background) {
2015 gdk_color_parse (b->status_background, &color);
2016 //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)
2017 if (uzbl.gui.main_window)
2018 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2019 else if (uzbl.gui.plug)
2020 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2023 if (b->title_format_long) {
2024 parsed = expand(b->title_format_long, 0);
2025 if (uzbl.gui.main_window)
2026 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2033 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2037 retreive_geometry();
2042 key_press_cb (GtkWidget* window, GdkEventKey* event)
2044 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2048 if (event->type != GDK_KEY_PRESS ||
2049 event->keyval == GDK_Page_Up ||
2050 event->keyval == GDK_Page_Down ||
2051 event->keyval == GDK_Up ||
2052 event->keyval == GDK_Down ||
2053 event->keyval == GDK_Left ||
2054 event->keyval == GDK_Right ||
2055 event->keyval == GDK_Shift_L ||
2056 event->keyval == GDK_Shift_R)
2059 /* turn off insert mode (if always_insert_mode is not used) */
2060 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2061 set_insert_mode(uzbl.behave.always_insert_mode);
2066 if (uzbl.behave.insert_mode &&
2067 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2068 (!uzbl.behave.modmask)
2073 if (event->keyval == GDK_Escape) {
2076 dehilight(uzbl.gui.web_view, NULL, NULL);
2080 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2081 if (event->keyval == GDK_Insert) {
2083 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2084 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2086 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2089 GString* keycmd = g_string_new(uzbl.state.keycmd);
2090 g_string_append (keycmd, str);
2091 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2098 if (event->keyval == GDK_BackSpace)
2099 keycmd_bs(NULL, NULL, NULL);
2101 gboolean key_ret = FALSE;
2102 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2105 GString* keycmd = g_string_new(uzbl.state.keycmd);
2106 g_string_append(keycmd, event->string);
2107 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2110 run_keycmd(key_ret);
2112 if (key_ret) return (!uzbl.behave.insert_mode);
2117 run_keycmd(const gboolean key_ret) {
2118 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2120 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2122 parse_command(act->name, act->param, NULL);
2126 /* try if it's an incremental keycmd or one that takes args, and run it */
2127 GString* short_keys = g_string_new ("");
2128 GString* short_keys_inc = g_string_new ("");
2130 guint len = strlen(uzbl.state.keycmd);
2131 for (i=0; i<len; i++) {
2132 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2133 g_string_assign(short_keys_inc, short_keys->str);
2134 g_string_append_c(short_keys, '_');
2135 g_string_append_c(short_keys_inc, '*');
2137 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2138 /* run normal cmds only if return was pressed */
2139 exec_paramcmd(act, i);
2142 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2143 if (key_ret) /* just quit the incremental command on return */
2145 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2149 g_string_truncate(short_keys, short_keys->len - 1);
2151 g_string_free (short_keys, TRUE);
2152 g_string_free (short_keys_inc, TRUE);
2156 exec_paramcmd(const Action *act, const guint i) {
2157 GString *parampart = g_string_new (uzbl.state.keycmd);
2158 GString *actionname = g_string_new ("");
2159 GString *actionparam = g_string_new ("");
2160 g_string_erase (parampart, 0, i+1);
2162 g_string_printf (actionname, act->name, parampart->str);
2164 g_string_printf (actionparam, act->param, parampart->str);
2165 parse_command(actionname->str, actionparam->str, NULL);
2166 g_string_free(actionname, TRUE);
2167 g_string_free(actionparam, TRUE);
2168 g_string_free(parampart, TRUE);
2176 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2177 //main_window_ref = g_object_ref(scrolled_window);
2178 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
2180 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2181 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2183 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2184 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2189 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2190 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2191 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2192 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2193 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2195 return scrolled_window;
2202 g->mainbar = gtk_hbox_new (FALSE, 0);
2204 /* keep a reference to the bar so we can re-pack it at runtime*/
2205 //sbar_ref = g_object_ref(g->mainbar);
2207 g->mainbar_label = gtk_label_new ("");
2208 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2209 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2210 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2211 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2212 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2213 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2219 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2220 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2221 gtk_widget_set_name (window, "Uzbl browser");
2222 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2223 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2224 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2231 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2232 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2233 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2240 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2242 If actname is one that calls an external command, this function will inject
2243 newargs in front of the user-provided args in that command line. They will
2244 come become after the body of the script (in sh) or after the name of
2245 the command to execute (in spawn).
2246 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2247 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2249 The return value consist of two strings: the action (sh, ...) and its args.
2251 If act is not one that calls an external command, then the given action merely
2254 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2255 /* Arrr! Here be memory leaks */
2256 gchar *actdup = g_strdup(actname);
2257 g_array_append_val(rets, actdup);
2259 if ((g_strcmp0(actname, "spawn") == 0) ||
2260 (g_strcmp0(actname, "sh") == 0) ||
2261 (g_strcmp0(actname, "sync_spawn") == 0) ||
2262 (g_strcmp0(actname, "sync_sh") == 0)) {
2264 GString *a = g_string_new("");
2265 gchar **spawnparts = split_quoted(origargs, FALSE);
2266 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2267 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2269 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2270 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2272 g_array_append_val(rets, a->str);
2273 g_string_free(a, FALSE);
2274 g_strfreev(spawnparts);
2276 gchar *origdup = g_strdup(origargs);
2277 g_array_append_val(rets, origdup);
2279 return (gchar**)g_array_free(rets, FALSE);
2283 run_handler (const gchar *act, const gchar *args) {
2284 /* Consider this code a temporary hack to make the handlers usable.
2285 In practice, all this splicing, injection, and reconstruction is
2286 inefficient, annoying and hard to manage. Potential pitfalls arise
2287 when the handler specific args 1) are not quoted (the handler
2288 callbacks should take care of this) 2) are quoted but interfere
2289 with the users' own quotation. A more ideal solution is
2290 to refactor parse_command so that it doesn't just take a string
2291 and execute it; rather than that, we should have a function which
2292 returns the argument vector parsed from the string. This vector
2293 could be modified (e.g. insert additional args into it) before
2294 passing it to the next function that actually executes it. Though
2295 it still isn't perfect for chain actions.. will reconsider & re-
2296 factor when I have the time. -duc */
2298 char **parts = g_strsplit(act, " ", 2);
2300 if (g_strcmp0(parts[0], "chain") == 0) {
2301 GString *newargs = g_string_new("");
2302 gchar **chainparts = split_quoted(parts[1], FALSE);
2304 /* for every argument in the chain, inject the handler args
2305 and make sure the new parts are wrapped in quotes */
2306 gchar **cp = chainparts;
2308 gchar *quotless = NULL;
2309 gchar **spliced_quotless = NULL; // sigh -_-;
2310 gchar **inpart = NULL;
2313 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2315 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2316 } else quotless = g_strdup(*cp);
2318 spliced_quotless = g_strsplit(quotless, " ", 2);
2319 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2320 g_strfreev(spliced_quotless);
2322 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2328 parse_command(parts[0], &(newargs->str[1]), NULL);
2329 g_string_free(newargs, TRUE);
2330 g_strfreev(chainparts);
2333 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2334 parse_command(inparts[0], inparts[1], NULL);
2342 add_binding (const gchar *key, const gchar *act) {
2343 char **parts = g_strsplit(act, " ", 2);
2350 if (uzbl.state.verbose)
2351 printf ("Binding %-10s : %s\n", key, act);
2352 action = new_action(parts[0], parts[1]);
2354 if (g_hash_table_remove (uzbl.bindings, key))
2355 g_warning ("Overwriting existing binding for \"%s\"", key);
2356 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2361 get_xdg_var (XDG_Var xdg) {
2362 const gchar* actual_value = getenv (xdg.environmental);
2363 const gchar* home = getenv ("HOME");
2364 gchar* return_value;
2366 if (! actual_value || strcmp (actual_value, "") == 0) {
2367 if (xdg.default_value) {
2368 return_value = str_replace ("~", home, xdg.default_value);
2370 return_value = NULL;
2373 return_value = str_replace("~", home, actual_value);
2376 return return_value;
2380 find_xdg_file (int xdg_type, char* filename) {
2381 /* xdg_type = 0 => config
2382 xdg_type = 1 => data
2383 xdg_type = 2 => cache*/
2385 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2386 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2389 gchar* temporary_string;
2393 if (! file_exists (temporary_file) && xdg_type != 2) {
2394 buf = get_xdg_var (XDG[3 + xdg_type]);
2395 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2398 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2399 g_free (temporary_file);
2400 temporary_file = g_strconcat (temporary_string, filename, NULL);
2404 //g_free (temporary_string); - segfaults.
2406 if (file_exists (temporary_file)) {
2407 return temporary_file;
2414 State *s = &uzbl.state;
2415 Network *n = &uzbl.net;
2417 for (i = 0; default_config[i].command != NULL; i++) {
2418 parse_cmd_line(default_config[i].command, NULL);
2421 if (g_strcmp0(s->config_file, "-") == 0) {
2422 s->config_file = NULL;
2426 else if (!s->config_file) {
2427 s->config_file = find_xdg_file (0, "/uzbl/config");
2430 if (s->config_file) {
2431 GArray* lines = read_file_by_line (s->config_file);
2435 while ((line = g_array_index(lines, gchar*, i))) {
2436 parse_cmd_line (line, NULL);
2440 g_array_free (lines, TRUE);
2442 if (uzbl.state.verbose)
2443 printf ("No configuration file loaded.\n");
2446 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2449 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: */