1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
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 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "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 "Path to config file or '-' for stdin", "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 /* associate command names to their properties */
84 typedef const struct {
85 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
86 the PTR() macro is kind of preventing this change at the moment. */
94 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
96 /* abbreviations to help keep the table's width humane */
97 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
98 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
103 } var_name_to_ptr[] = {
104 /* variable name pointer to variable in code type dump callback function */
105 /* ---------------------------------------------------------------------------------------------- */
106 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
107 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
108 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
109 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
110 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
111 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
112 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
113 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
114 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
115 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
116 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
117 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
118 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
119 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
120 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
121 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
122 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
124 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
125 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
126 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
127 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
128 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
129 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
130 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
131 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
132 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
133 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
134 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
135 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
136 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
137 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, NULL)},
138 { "scheme_handler", PTR_V(uzbl.behave.scheme_handler, STR, 1, cmd_scheme_handler)},
139 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
140 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
141 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
142 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
143 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
144 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
145 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
146 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
148 /* exported WebKitWebSettings properties */
149 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
150 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
151 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
152 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
153 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
154 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
155 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
156 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
157 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
158 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
159 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
160 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
161 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
162 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
163 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
164 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
166 /* constants (not dumpable or writeable) */
167 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
168 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
169 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
170 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
171 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
172 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
173 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
174 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
175 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
176 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
177 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
179 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
180 }, *n2v_p = var_name_to_ptr;
187 { "SHIFT", GDK_SHIFT_MASK }, // shift
188 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
189 { "CONTROL", GDK_CONTROL_MASK }, // control
190 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
191 { "MOD2", GDK_MOD2_MASK }, // 5th mod
192 { "MOD3", GDK_MOD3_MASK }, // 6th mod
193 { "MOD4", GDK_MOD4_MASK }, // 7th mod
194 { "MOD5", GDK_MOD5_MASK }, // 8th mod
195 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
196 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
197 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
198 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
199 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
200 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
201 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
202 { "META", GDK_META_MASK }, // meta (since 2.10)
207 /* construct a hash from the var_name_to_ptr array for quick access */
209 make_var_to_name_hash() {
210 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
212 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
217 /* --- UTILITY FUNCTIONS --- */
218 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
220 get_exp_type(gchar *s) {
224 else if(*(s+1) == '{')
225 return EXP_BRACED_VAR;
226 else if(*(s+1) == '<')
228 else if(*(s+1) == '[')
231 return EXP_SIMPLE_VAR;
237 * recurse == 1: don't expand '@(command)@'
238 * recurse == 2: don't expand '@<java script>@'
241 expand(char *s, guint recurse) {
245 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
250 gchar *cmd_stdout = NULL;
252 GString *buf = g_string_new("");
253 GString *js_ret = g_string_new("");
258 g_string_append_c(buf, *++s);
263 etype = get_exp_type(s);
268 vend = strpbrk(s, end_simple_var);
269 if(!vend) vend = strchr(s, '\0');
273 vend = strchr(s, upto);
274 if(!vend) vend = strchr(s, '\0');
278 strcpy(str_end, ")@");
280 vend = strstr(s, str_end);
281 if(!vend) vend = strchr(s, '\0');
285 strcpy(str_end, ">@");
287 vend = strstr(s, str_end);
288 if(!vend) vend = strchr(s, '\0');
292 strcpy(str_end, "]@");
294 vend = strstr(s, str_end);
295 if(!vend) vend = strchr(s, '\0');
300 strncpy(ret, s, vend-s);
304 if(etype == EXP_SIMPLE_VAR ||
305 etype == EXP_BRACED_VAR) {
306 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
307 if(c->type == TYPE_STR && *c->ptr != NULL) {
308 g_string_append(buf, (gchar *)*c->ptr);
309 } else if(c->type == TYPE_INT) {
310 g_string_append_printf(buf, "%d", (int)*c->ptr);
312 else if(c->type == TYPE_FLOAT) {
313 g_string_append_printf(buf, "%f", *(float *)c->ptr);
317 if(etype == EXP_SIMPLE_VAR)
322 else if(recurse != 1 &&
324 mycmd = expand(ret, 1);
325 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
329 g_printerr("error on running command: %s\n", err->message);
332 else if (*cmd_stdout) {
333 int len = strlen(cmd_stdout);
335 if(cmd_stdout[len-1] == '\n')
336 cmd_stdout[--len] = 0; /* strip trailing newline */
338 g_string_append(buf, cmd_stdout);
343 else if(recurse != 2 &&
345 mycmd = expand(ret, 2);
346 eval_js(uzbl.gui.web_view, mycmd, js_ret);
350 g_string_append(buf, js_ret->str);
351 g_string_free(js_ret, TRUE);
352 js_ret = g_string_new("");
356 else if(etype == EXP_ESCAPE) {
357 mycmd = expand(ret, 0);
358 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
360 g_string_append(buf, escaped);
369 g_string_append_c(buf, *s);
374 g_string_free(js_ret, TRUE);
375 return g_string_free(buf, FALSE);
382 snprintf(tmp, sizeof(tmp), "%i", val);
383 return g_strdup(tmp);
387 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
390 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
393 str_replace (const char* search, const char* replace, const char* string) {
397 buf = g_strsplit (string, search, -1);
398 ret = g_strjoinv (replace, buf);
399 g_strfreev(buf); // somebody said this segfaults
405 read_file_by_line (gchar *path) {
406 GIOChannel *chan = NULL;
407 gchar *readbuf = NULL;
409 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
412 chan = g_io_channel_new_file(path, "r", NULL);
415 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
416 const gchar* val = g_strdup (readbuf);
417 g_array_append_val (lines, val);
422 g_io_channel_unref (chan);
424 fprintf(stderr, "File '%s' not be read.\n", path);
431 parseenv (char* string) {
432 extern char** environ;
433 gchar* tmpstr = NULL;
437 while (environ[i] != NULL) {
438 gchar** env = g_strsplit (environ[i], "=", 2);
439 gchar* envname = g_strconcat ("$", env[0], NULL);
441 if (g_strrstr (string, envname) != NULL) {
442 tmpstr = g_strdup(string);
444 string = str_replace(envname, env[1], tmpstr);
449 g_strfreev (env); // somebody said this breaks uzbl
457 setup_signal(int signr, sigfunc *shandler) {
458 struct sigaction nh, oh;
460 nh.sa_handler = shandler;
461 sigemptyset(&nh.sa_mask);
464 if(sigaction(signr, &nh, &oh) < 0)
472 if (uzbl.behave.fifo_dir)
473 unlink (uzbl.comm.fifo_path);
474 if (uzbl.behave.socket_dir)
475 unlink (uzbl.comm.socket_path);
477 g_free(uzbl.state.executable_path);
478 g_free(uzbl.state.keycmd);
479 g_hash_table_destroy(uzbl.bindings);
480 g_hash_table_destroy(uzbl.behave.commands);
483 /* used for html_mode_timeout
484 * be sure to extend this function to use
485 * more timers if needed in other places
488 set_timeout(int seconds) {
490 memset(&t, 0, sizeof t);
492 t.it_value.tv_sec = seconds;
493 t.it_value.tv_usec = 0;
494 setitimer(ITIMER_REAL, &t, NULL);
497 /* --- SIGNAL HANDLER --- */
500 catch_sigterm(int s) {
506 catch_sigint(int s) {
516 set_var_value("mode", "0");
521 /* --- CALLBACKS --- */
524 navigation_decision_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527 (void) navigation_action;
530 const gchar* uri = webkit_network_request_get_uri (request);
531 gboolean decision_made = FALSE;
533 if (uzbl.state.verbose)
534 printf("Navigation requested -> %s\n", uri);
536 if (uzbl.behave.scheme_handler) {
537 GString *s = g_string_new ("");
538 g_string_printf(s, "'%s'", uri);
540 run_handler(uzbl.behave.scheme_handler, s->str);
542 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
543 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
544 if ( p != NULL ) *p = '\0';
545 if (!strcmp(uzbl.comm.sync_stdout, "USED")) {
546 webkit_web_policy_decision_ignore(policy_decision);
547 decision_made = TRUE;
550 if (uzbl.comm.sync_stdout)
551 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
553 g_string_free(s, TRUE);
556 webkit_web_policy_decision_use(policy_decision);
562 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
565 (void) navigation_action;
566 (void) policy_decision;
568 const gchar* uri = webkit_network_request_get_uri (request);
569 if (uzbl.state.verbose)
570 printf("New window requested -> %s \n", uri);
571 webkit_web_policy_decision_use(policy_decision);
576 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
581 /* If we can display it, let's display it... */
582 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
583 webkit_web_policy_decision_use (policy_decision);
587 /* ...everything we can't displayed is downloaded */
588 webkit_web_policy_decision_download (policy_decision);
593 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
597 if (uzbl.state.selected_url != NULL) {
598 if (uzbl.state.verbose)
599 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
600 new_window_load_uri(uzbl.state.selected_url);
602 if (uzbl.state.verbose)
603 printf("New web view -> %s\n","Nothing to open, exiting");
609 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
612 if (uzbl.behave.download_handler) {
613 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
614 if (uzbl.state.verbose)
615 printf("Download -> %s\n",uri);
616 /* if urls not escaped, we may have to escape and quote uri before this call */
617 run_handler(uzbl.behave.download_handler, uri);
622 /* scroll a bar in a given direction */
624 scroll (GtkAdjustment* bar, GArray *argv) {
628 gdouble page_size = gtk_adjustment_get_page_size(bar);
629 gdouble value = gtk_adjustment_get_value(bar);
630 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
633 value += page_size * amount * 0.01;
637 max_value = gtk_adjustment_get_upper(bar) - page_size;
639 if (value > max_value)
640 value = max_value; /* don't scroll past the end of the page */
642 gtk_adjustment_set_value (bar, value);
646 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
647 (void) page; (void) argv; (void) result;
648 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
652 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
653 (void) page; (void) argv; (void) result;
654 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
655 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
659 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
660 (void) page; (void) result;
661 scroll(uzbl.gui.bar_v, argv);
665 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
666 (void) page; (void) result;
667 scroll(uzbl.gui.bar_h, argv);
672 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
673 if(uzbl.state.verbose)
674 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
676 /* update geometry var with the actual geometry
677 this is necessary as some WMs don't seem to honour
678 the above setting and we don't want to end up with
679 wrong geometry information
686 if (!uzbl.behave.show_status) {
687 gtk_widget_hide(uzbl.gui.mainbar);
689 gtk_widget_show(uzbl.gui.mainbar);
695 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
700 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
704 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
709 if (uzbl.behave.show_status) {
710 gtk_widget_hide(uzbl.gui.mainbar);
712 gtk_widget_show(uzbl.gui.mainbar);
714 uzbl.behave.show_status = !uzbl.behave.show_status;
719 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
723 //Set selected_url state variable
724 g_free(uzbl.state.selected_url);
725 uzbl.state.selected_url = NULL;
727 uzbl.state.selected_url = g_strdup(link);
733 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
736 const gchar *title = webkit_web_view_get_title(web_view);
737 if (uzbl.gui.main_title)
738 g_free (uzbl.gui.main_title);
739 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
744 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
747 uzbl.gui.sbar.load_progress = progress;
749 g_free(uzbl.gui.sbar.progress_bar);
750 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
756 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
760 if (uzbl.behave.load_finish_handler)
761 run_handler(uzbl.behave.load_finish_handler, "");
764 void clear_keycmd() {
765 g_free(uzbl.state.keycmd);
766 uzbl.state.keycmd = g_strdup("");
770 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
774 uzbl.gui.sbar.load_progress = 0;
775 clear_keycmd(); // don't need old commands to remain on new page?
776 if (uzbl.behave.load_start_handler)
777 run_handler(uzbl.behave.load_start_handler, "");
781 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
784 g_free (uzbl.state.uri);
785 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
786 uzbl.state.uri = g_string_free (newuri, FALSE);
787 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
788 set_insert_mode(uzbl.behave.always_insert_mode);
791 if (uzbl.behave.load_commit_handler)
792 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
796 destroy_cb (GtkWidget* widget, gpointer data) {
804 if (uzbl.behave.history_handler) {
806 struct tm * timeinfo;
809 timeinfo = localtime ( &rawtime );
810 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
811 run_handler(uzbl.behave.history_handler, date);
816 /* VIEW funcs (little webkit wrappers) */
817 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
819 VIEWFUNC(reload_bypass_cache)
820 VIEWFUNC(stop_loading)
827 /* -- command to callback/function map for things we cannot attach to any signals */
828 struct {char *key; CommandInfo value;} cmdlist[] =
829 { /* key function no_split */
830 { "back", {view_go_back, 0} },
831 { "forward", {view_go_forward, 0} },
832 { "scroll_vert", {scroll_vert, 0} },
833 { "scroll_horz", {scroll_horz, 0} },
834 { "scroll_begin", {scroll_begin, 0} },
835 { "scroll_end", {scroll_end, 0} },
836 { "reload", {view_reload, 0}, },
837 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
838 { "stop", {view_stop_loading, 0}, },
839 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
840 { "zoom_out", {view_zoom_out, 0}, },
841 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
842 { "uri", {load_uri, TRUE} },
843 { "js", {run_js, TRUE} },
844 { "script", {run_external_js, 0} },
845 { "toggle_status", {toggle_status_cb, 0} },
846 { "spawn", {spawn, 0} },
847 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
848 { "sh", {spawn_sh, 0} },
849 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
850 { "exit", {close_uzbl, 0} },
851 { "search", {search_forward_text, TRUE} },
852 { "search_reverse", {search_reverse_text, TRUE} },
853 { "dehilight", {dehilight, 0} },
854 { "toggle_insert_mode", {toggle_insert_mode, 0} },
855 { "set", {set_var, TRUE} },
856 //{ "get", {get_var, TRUE} },
857 { "bind", {act_bind, TRUE} },
858 { "dump_config", {act_dump_config, 0} },
859 { "keycmd", {keycmd, TRUE} },
860 { "keycmd_nl", {keycmd_nl, TRUE} },
861 { "keycmd_bs", {keycmd_bs, 0} },
862 { "chain", {chain, 0} },
863 { "print", {print, TRUE} }
870 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
872 for (i = 0; i < LENGTH(cmdlist); i++)
873 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
876 /* -- CORE FUNCTIONS -- */
879 free_action(gpointer act) {
880 Action *action = (Action*)act;
881 g_free(action->name);
883 g_free(action->param);
888 new_action(const gchar *name, const gchar *param) {
889 Action *action = g_new(Action, 1);
891 action->name = g_strdup(name);
893 action->param = g_strdup(param);
895 action->param = NULL;
901 file_exists (const char * filename) {
902 return (access(filename, F_OK) == 0);
906 set_var(WebKitWebView *page, GArray *argv, GString *result) {
907 (void) page; (void) result;
908 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
909 if (split[0] != NULL) {
910 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
911 set_var_value(g_strstrip(split[0]), value);
918 print(WebKitWebView *page, GArray *argv, GString *result) {
919 (void) page; (void) result;
922 buf = expand(argv_idx(argv, 0), 0);
923 g_string_assign(result, buf);
928 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
929 (void) page; (void) result;
930 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
931 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
932 add_binding(g_strstrip(split[0]), value);
950 set_mode_indicator() {
951 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
952 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
957 set_mode_indicator();
962 set_insert_mode(gboolean mode) {
963 uzbl.behave.insert_mode = mode;
964 set_mode_indicator();
968 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
969 (void) page; (void) result;
971 if (argv_idx(argv, 0)) {
972 if (strcmp (argv_idx(argv, 0), "0") == 0) {
973 set_insert_mode(FALSE);
975 set_insert_mode(TRUE);
978 set_insert_mode( !uzbl.behave.insert_mode );
985 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
988 if (argv_idx(argv, 0)) {
989 GString* newuri = g_string_new (argv_idx(argv, 0));
990 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
991 run_js(web_view, argv, NULL);
994 if (!soup_uri_new(argv_idx(argv, 0)))
995 g_string_prepend (newuri, "http://");
996 /* if we do handle cookies, ask our handler for them */
997 webkit_web_view_load_uri (web_view, newuri->str);
998 g_string_free (newuri, TRUE);
1005 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
1006 size_t argumentCount, const JSValueRef arguments[],
1007 JSValueRef* exception) {
1012 JSStringRef js_result_string;
1013 GString *result = g_string_new("");
1015 if (argumentCount >= 1) {
1016 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
1017 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
1018 char ctl_line[arg_size];
1019 JSStringGetUTF8CString(arg, ctl_line, arg_size);
1021 parse_cmd_line(ctl_line, result);
1023 JSStringRelease(arg);
1025 js_result_string = JSStringCreateWithUTF8CString(result->str);
1027 g_string_free(result, TRUE);
1029 return JSValueMakeString(ctx, js_result_string);
1032 JSStaticFunction js_static_functions[] = {
1033 {"run", js_run_command, kJSPropertyAttributeNone},
1038 /* This function creates the class and its definition, only once */
1039 if (!uzbl.js.initialized) {
1040 /* it would be pretty cool to make this dynamic */
1041 uzbl.js.classdef = kJSClassDefinitionEmpty;
1042 uzbl.js.classdef.staticFunctions = js_static_functions;
1044 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1050 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1051 WebKitWebFrame *frame;
1052 JSGlobalContextRef context;
1053 JSObjectRef globalobject;
1054 JSStringRef var_name;
1056 JSStringRef js_script;
1057 JSValueRef js_result;
1058 JSStringRef js_result_string;
1059 size_t js_result_size;
1063 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1064 context = webkit_web_frame_get_global_context(frame);
1065 globalobject = JSContextGetGlobalObject(context);
1067 /* uzbl javascript namespace */
1068 var_name = JSStringCreateWithUTF8CString("Uzbl");
1069 JSObjectSetProperty(context, globalobject, var_name,
1070 JSObjectMake(context, uzbl.js.classref, NULL),
1071 kJSClassAttributeNone, NULL);
1073 /* evaluate the script and get return value*/
1074 js_script = JSStringCreateWithUTF8CString(script);
1075 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1076 if (js_result && !JSValueIsUndefined(context, js_result)) {
1077 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1078 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1080 if (js_result_size) {
1081 char js_result_utf8[js_result_size];
1082 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1083 g_string_assign(result, js_result_utf8);
1086 JSStringRelease(js_result_string);
1090 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1092 JSStringRelease(var_name);
1093 JSStringRelease(js_script);
1097 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1098 if (argv_idx(argv, 0))
1099 eval_js(web_view, argv_idx(argv, 0), result);
1103 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1105 if (argv_idx(argv, 0)) {
1106 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1111 while ((line = g_array_index(lines, gchar*, i))) {
1113 js = g_strdup (line);
1115 gchar* newjs = g_strconcat (js, line, NULL);
1122 if (uzbl.state.verbose)
1123 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1125 if (argv_idx (argv, 1)) {
1126 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1130 eval_js (web_view, js, result);
1132 g_array_free (lines, TRUE);
1137 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1138 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1139 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1140 webkit_web_view_unmark_text_matches (page);
1141 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1142 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1146 if (uzbl.state.searchtx) {
1147 if (uzbl.state.verbose)
1148 printf ("Searching: %s\n", uzbl.state.searchtx);
1149 webkit_web_view_set_highlight_text_matches (page, TRUE);
1150 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1155 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1157 search_text(page, argv, TRUE);
1161 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1163 search_text(page, argv, FALSE);
1167 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1168 (void) argv; (void) result;
1169 webkit_web_view_set_highlight_text_matches (page, FALSE);
1174 new_window_load_uri (const gchar * uri) {
1175 if (uzbl.behave.new_window) {
1176 GString *s = g_string_new ("");
1177 g_string_printf(s, "'%s'", uri);
1178 run_handler(uzbl.behave.new_window, s->str);
1181 GString* to_execute = g_string_new ("");
1182 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1184 for (i = 0; entries[i].long_name != NULL; i++) {
1185 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1186 gchar** str = (gchar**)entries[i].arg_data;
1188 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1192 if (uzbl.state.verbose)
1193 printf("\n%s\n", to_execute->str);
1194 g_spawn_command_line_async (to_execute->str, NULL);
1195 g_string_free (to_execute, TRUE);
1199 chain (WebKitWebView *page, GArray *argv, GString *result) {
1200 (void) page; (void) result;
1202 gchar **parts = NULL;
1204 while ((a = argv_idx(argv, i++))) {
1205 parts = g_strsplit (a, " ", 2);
1207 parse_command(parts[0], parts[1], result);
1213 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1217 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1223 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1227 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1233 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1238 int len = strlen(uzbl.state.keycmd);
1239 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1241 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1246 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1253 /* --Statusbar functions-- */
1255 build_progressbar_ascii(int percent) {
1256 int width=uzbl.gui.sbar.progress_w;
1259 GString *bar = g_string_new("");
1261 l = (double)percent*((double)width/100.);
1262 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1264 for(i=0; i<(int)l; i++)
1265 g_string_append(bar, uzbl.gui.sbar.progress_s);
1268 g_string_append(bar, uzbl.gui.sbar.progress_u);
1270 return g_string_free(bar, FALSE);
1272 /* --End Statusbar functions-- */
1275 sharg_append(GArray *a, const gchar *str) {
1276 const gchar *s = (str ? str : "");
1277 g_array_append_val(a, s);
1280 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1282 run_command (const gchar *command, const guint npre, const gchar **args,
1283 const gboolean sync, char **output_stdout) {
1284 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1287 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1288 gchar *pid = itos(getpid());
1289 gchar *xwin = itos(uzbl.xwin);
1291 sharg_append(a, command);
1292 for (i = 0; i < npre; i++) /* add n args before the default vars */
1293 sharg_append(a, args[i]);
1294 sharg_append(a, uzbl.state.config_file);
1295 sharg_append(a, pid);
1296 sharg_append(a, xwin);
1297 sharg_append(a, uzbl.comm.fifo_path);
1298 sharg_append(a, uzbl.comm.socket_path);
1299 sharg_append(a, uzbl.state.uri);
1300 sharg_append(a, uzbl.gui.main_title);
1302 for (i = npre; i < g_strv_length((gchar**)args); i++)
1303 sharg_append(a, args[i]);
1307 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1309 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1310 NULL, NULL, output_stdout, NULL, NULL, &err);
1311 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1312 NULL, NULL, NULL, &err);
1314 if (uzbl.state.verbose) {
1315 GString *s = g_string_new("spawned:");
1316 for (i = 0; i < (a->len); i++) {
1317 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1318 g_string_append_printf(s, " %s", qarg);
1321 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1322 printf("%s\n", s->str);
1323 g_string_free(s, TRUE);
1325 printf("Stdout: %s\n", *output_stdout);
1329 g_printerr("error on run_command: %s\n", err->message);
1334 g_array_free (a, TRUE);
1339 split_quoted(const gchar* src, const gboolean unquote) {
1340 /* split on unquoted space, return array of strings;
1341 remove a layer of quotes and backslashes if unquote */
1342 if (!src) return NULL;
1344 gboolean dq = FALSE;
1345 gboolean sq = FALSE;
1346 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1347 GString *s = g_string_new ("");
1351 for (p = src; *p != '\0'; p++) {
1352 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1353 else if (*p == '\\') { g_string_append_c(s, *p++);
1354 g_string_append_c(s, *p); }
1355 else if ((*p == '"') && unquote && !sq) dq = !dq;
1356 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1358 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1359 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1361 else if ((*p == ' ') && !dq && !sq) {
1362 dup = g_strdup(s->str);
1363 g_array_append_val(a, dup);
1364 g_string_truncate(s, 0);
1365 } else g_string_append_c(s, *p);
1367 dup = g_strdup(s->str);
1368 g_array_append_val(a, dup);
1369 ret = (gchar**)a->data;
1370 g_array_free (a, FALSE);
1371 g_string_free (s, TRUE);
1376 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1377 (void)web_view; (void)result;
1378 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1379 if (argv_idx(argv, 0))
1380 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1384 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1385 (void)web_view; (void)result;
1387 if (argv_idx(argv, 0))
1388 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1389 TRUE, &uzbl.comm.sync_stdout);
1393 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1394 (void)web_view; (void)result;
1395 if (!uzbl.behave.shell_cmd) {
1396 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1401 gchar *spacer = g_strdup("");
1402 g_array_insert_val(argv, 1, spacer);
1403 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1405 for (i = 1; i < g_strv_length(cmd); i++)
1406 g_array_prepend_val(argv, cmd[i]);
1408 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1414 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1415 (void)web_view; (void)result;
1416 if (!uzbl.behave.shell_cmd) {
1417 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1422 gchar *spacer = g_strdup("");
1423 g_array_insert_val(argv, 1, spacer);
1424 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1426 for (i = 1; i < g_strv_length(cmd); i++)
1427 g_array_prepend_val(argv, cmd[i]);
1429 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1430 TRUE, &uzbl.comm.sync_stdout);
1436 parse_command(const char *cmd, const char *param, GString *result) {
1439 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1441 gchar **par = split_quoted(param, TRUE);
1442 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1444 if (c->no_split) { /* don't split */
1445 sharg_append(a, param);
1447 for (i = 0; i < g_strv_length(par); i++)
1448 sharg_append(a, par[i]);
1451 if (result == NULL) {
1452 GString *result_print = g_string_new("");
1454 c->function(uzbl.gui.web_view, a, result_print);
1455 if (result_print->len)
1456 printf("%*s\n", result_print->len, result_print->str);
1458 g_string_free(result_print, TRUE);
1460 c->function(uzbl.gui.web_view, a, result);
1463 g_array_free (a, TRUE);
1466 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1473 if(*uzbl.net.proxy_url == ' '
1474 || uzbl.net.proxy_url == NULL) {
1475 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1476 (GType) SOUP_SESSION_PROXY_URI);
1479 suri = soup_uri_new(uzbl.net.proxy_url);
1480 g_object_set(G_OBJECT(uzbl.net.soup_session),
1481 SOUP_SESSION_PROXY_URI,
1483 soup_uri_free(suri);
1490 if(file_exists(uzbl.gui.icon)) {
1491 if (uzbl.gui.main_window)
1492 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1494 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1500 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1501 g_array_append_val (a, uzbl.state.uri);
1502 load_uri(uzbl.gui.web_view, a, NULL);
1503 g_array_free (a, TRUE);
1507 cmd_always_insert_mode() {
1508 set_insert_mode(uzbl.behave.always_insert_mode);
1514 g_object_set(G_OBJECT(uzbl.net.soup_session),
1515 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1519 cmd_max_conns_host() {
1520 g_object_set(G_OBJECT(uzbl.net.soup_session),
1521 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1526 soup_session_remove_feature
1527 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1528 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1529 /*g_free(uzbl.net.soup_logger);*/
1531 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1532 soup_session_add_feature(uzbl.net.soup_session,
1533 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1538 return webkit_web_view_get_settings(uzbl.gui.web_view);
1543 WebKitWebSettings *ws = view_settings();
1544 if (uzbl.behave.font_size > 0) {
1545 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1548 if (uzbl.behave.monospace_size > 0) {
1549 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1550 uzbl.behave.monospace_size, NULL);
1552 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1553 uzbl.behave.font_size, NULL);
1559 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1563 cmd_disable_plugins() {
1564 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1565 !uzbl.behave.disable_plugins, NULL);
1569 cmd_disable_scripts() {
1570 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1571 !uzbl.behave.disable_scripts, NULL);
1575 cmd_minimum_font_size() {
1576 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1577 uzbl.behave.minimum_font_size, NULL);
1580 cmd_autoload_img() {
1581 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1582 uzbl.behave.autoload_img, NULL);
1587 cmd_autoshrink_img() {
1588 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1589 uzbl.behave.autoshrink_img, NULL);
1594 cmd_enable_spellcheck() {
1595 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1596 uzbl.behave.enable_spellcheck, NULL);
1600 cmd_enable_private() {
1601 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1602 uzbl.behave.enable_private, NULL);
1607 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1608 uzbl.behave.print_bg, NULL);
1613 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1614 uzbl.behave.style_uri, NULL);
1618 cmd_resizable_txt() {
1619 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1620 uzbl.behave.resizable_txt, NULL);
1624 cmd_default_encoding() {
1625 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1626 uzbl.behave.default_encoding, NULL);
1630 cmd_enforce_96dpi() {
1631 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1632 uzbl.behave.enforce_96dpi, NULL);
1636 cmd_caret_browsing() {
1637 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1638 uzbl.behave.caret_browsing, NULL);
1642 cmd_cookie_handler() {
1643 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1644 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1645 if ((g_strcmp0(split[0], "sh") == 0) ||
1646 (g_strcmp0(split[0], "spawn") == 0)) {
1647 g_free (uzbl.behave.cookie_handler);
1648 uzbl.behave.cookie_handler =
1649 g_strdup_printf("sync_%s %s", split[0], split[1]);
1655 cmd_scheme_handler() {
1656 gchar **split = g_strsplit(uzbl.behave.scheme_handler, " ", 2);
1657 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1658 if ((g_strcmp0(split[0], "sh") == 0) ||
1659 (g_strcmp0(split[0], "spawn") == 0)) {
1660 g_free (uzbl.behave.scheme_handler);
1661 uzbl.behave.scheme_handler =
1662 g_strdup_printf("sync_%s %s", split[0], split[1]);
1669 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1674 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1679 if(uzbl.behave.inject_html) {
1680 webkit_web_view_load_html_string (uzbl.gui.web_view,
1681 uzbl.behave.inject_html, NULL);
1690 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1691 uzbl.behave.modmask = 0;
1693 if(uzbl.behave.modkey)
1694 g_free(uzbl.behave.modkey);
1695 uzbl.behave.modkey = buf;
1697 for (i = 0; modkeys[i].key != NULL; i++) {
1698 if (g_strrstr(buf, modkeys[i].key))
1699 uzbl.behave.modmask |= modkeys[i].mask;
1705 if (*uzbl.net.useragent == ' ') {
1706 g_free (uzbl.net.useragent);
1707 uzbl.net.useragent = NULL;
1709 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1710 uzbl.net.useragent, NULL);
1716 gtk_widget_ref(uzbl.gui.scrolled_win);
1717 gtk_widget_ref(uzbl.gui.mainbar);
1718 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1719 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1721 if(uzbl.behave.status_top) {
1722 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1723 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1726 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1727 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1729 gtk_widget_unref(uzbl.gui.scrolled_win);
1730 gtk_widget_unref(uzbl.gui.mainbar);
1731 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1736 set_var_value(gchar *name, gchar *val) {
1737 uzbl_cmdprop *c = NULL;
1741 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1742 if(!c->writeable) return FALSE;
1744 /* check for the variable type */
1745 if (c->type == TYPE_STR) {
1746 buf = expand(val, 0);
1749 } else if(c->type == TYPE_INT) {
1750 int *ip = (int *)c->ptr;
1751 buf = expand(val, 0);
1752 *ip = (int)strtoul(buf, &endp, 10);
1754 } else if (c->type == TYPE_FLOAT) {
1755 float *fp = (float *)c->ptr;
1756 buf = expand(val, 0);
1757 *fp = strtod(buf, &endp);
1761 /* invoke a command specific function */
1762 if(c->func) c->func();
1769 Behaviour *b = &uzbl.behave;
1771 if(b->html_buffer->str) {
1772 webkit_web_view_load_html_string (uzbl.gui.web_view,
1773 b->html_buffer->str, b->base_url);
1774 g_string_free(b->html_buffer, TRUE);
1775 b->html_buffer = g_string_new("");
1779 enum {M_CMD, M_HTML};
1781 parse_cmd_line(const char *ctl_line, GString *result) {
1782 Behaviour *b = &uzbl.behave;
1785 if(b->mode == M_HTML) {
1786 len = strlen(b->html_endmarker);
1787 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1788 if(len == strlen(ctl_line)-1 &&
1789 !strncmp(b->html_endmarker, ctl_line, len)) {
1791 set_var_value("mode", "0");
1796 set_timeout(b->html_timeout);
1797 g_string_append(b->html_buffer, ctl_line);
1800 else if((ctl_line[0] == '#') /* Comments */
1801 || (ctl_line[0] == ' ')
1802 || (ctl_line[0] == '\n'))
1803 ; /* ignore these lines */
1804 else { /* parse a command */
1806 gchar **tokens = NULL;
1807 len = strlen(ctl_line);
1809 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1810 ctlstrip = g_strndup(ctl_line, len - 1);
1811 else ctlstrip = g_strdup(ctl_line);
1813 tokens = g_strsplit(ctlstrip, " ", 2);
1814 parse_command(tokens[0], tokens[1], result);
1821 build_stream_name(int type, const gchar* dir) {
1822 State *s = &uzbl.state;
1826 str = g_strdup_printf
1827 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1828 } else if (type == SOCKET) {
1829 str = g_strdup_printf
1830 ("%s/uzbl_socket_%s", dir, s->instance_name);
1836 control_fifo(GIOChannel *gio, GIOCondition condition) {
1837 if (uzbl.state.verbose)
1838 printf("triggered\n");
1843 if (condition & G_IO_HUP)
1844 g_error ("Fifo: Read end of pipe died!\n");
1847 g_error ("Fifo: GIOChannel broke\n");
1849 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1850 if (ret == G_IO_STATUS_ERROR) {
1851 g_error ("Fifo: Error reading: %s\n", err->message);
1855 parse_cmd_line(ctl_line, NULL);
1862 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1863 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1864 if (unlink(uzbl.comm.fifo_path) == -1)
1865 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1866 g_free(uzbl.comm.fifo_path);
1867 uzbl.comm.fifo_path = NULL;
1870 GIOChannel *chan = NULL;
1871 GError *error = NULL;
1872 gchar *path = build_stream_name(FIFO, dir);
1874 if (!file_exists(path)) {
1875 if (mkfifo (path, 0666) == 0) {
1876 // 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.
1877 chan = g_io_channel_new_file(path, "r+", &error);
1879 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1880 if (uzbl.state.verbose)
1881 printf ("init_fifo: created successfully as %s\n", path);
1882 uzbl.comm.fifo_path = path;
1884 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1885 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1886 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1887 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1889 /* if we got this far, there was an error; cleanup */
1890 if (error) g_error_free (error);
1897 control_stdin(GIOChannel *gio, GIOCondition condition) {
1899 gchar *ctl_line = NULL;
1902 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1903 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1906 parse_cmd_line(ctl_line, NULL);
1914 GIOChannel *chan = NULL;
1915 GError *error = NULL;
1917 chan = g_io_channel_unix_new(fileno(stdin));
1919 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1920 g_error ("Stdin: could not add watch\n");
1922 if (uzbl.state.verbose)
1923 printf ("Stdin: watch added successfully\n");
1926 g_error ("Stdin: Error while opening: %s\n", error->message);
1928 if (error) g_error_free (error);
1932 control_socket(GIOChannel *chan) {
1933 struct sockaddr_un remote;
1934 unsigned int t = sizeof(remote);
1936 GIOChannel *clientchan;
1938 clientsock = accept (g_io_channel_unix_get_fd(chan),
1939 (struct sockaddr *) &remote, &t);
1941 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1942 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1943 (GIOFunc) control_client_socket, clientchan);
1950 control_client_socket(GIOChannel *clientchan) {
1952 GString *result = g_string_new("");
1953 GError *error = NULL;
1957 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1958 if (ret == G_IO_STATUS_ERROR) {
1959 g_warning ("Error reading: %s\n", error->message);
1960 g_io_channel_shutdown(clientchan, TRUE, &error);
1962 } else if (ret == G_IO_STATUS_EOF) {
1963 /* shutdown and remove channel watch from main loop */
1964 g_io_channel_shutdown(clientchan, TRUE, &error);
1969 parse_cmd_line (ctl_line, result);
1970 g_string_append_c(result, '\n');
1971 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1973 if (ret == G_IO_STATUS_ERROR) {
1974 g_warning ("Error writing: %s", error->message);
1976 g_io_channel_flush(clientchan, &error);
1979 if (error) g_error_free (error);
1980 g_string_free(result, TRUE);
1986 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1987 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1988 if (unlink(uzbl.comm.socket_path) == -1)
1989 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1990 g_free(uzbl.comm.socket_path);
1991 uzbl.comm.socket_path = NULL;
1999 GIOChannel *chan = NULL;
2001 struct sockaddr_un local;
2002 gchar *path = build_stream_name(SOCKET, dir);
2004 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2006 local.sun_family = AF_UNIX;
2007 strcpy (local.sun_path, path);
2008 unlink (local.sun_path);
2010 len = strlen (local.sun_path) + sizeof (local.sun_family);
2011 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2012 if (uzbl.state.verbose)
2013 printf ("init_socket: opened in %s\n", path);
2016 if( (chan = g_io_channel_unix_new(sock)) ) {
2017 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2018 uzbl.comm.socket_path = path;
2021 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2023 /* if we got this far, there was an error; cleanup */
2030 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2031 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2033 // this function may be called very early when the templates are not set (yet), hence the checks
2035 update_title (void) {
2036 Behaviour *b = &uzbl.behave;
2039 if (b->show_status) {
2040 if (b->title_format_short) {
2041 parsed = expand(b->title_format_short, 0);
2042 if (uzbl.gui.main_window)
2043 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2046 if (b->status_format) {
2047 parsed = expand(b->status_format, 0);
2048 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2051 if (b->status_background) {
2053 gdk_color_parse (b->status_background, &color);
2054 //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)
2055 if (uzbl.gui.main_window)
2056 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2057 else if (uzbl.gui.plug)
2058 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2061 if (b->title_format_long) {
2062 parsed = expand(b->title_format_long, 0);
2063 if (uzbl.gui.main_window)
2064 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2071 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2075 retreive_geometry();
2080 key_press_cb (GtkWidget* window, GdkEventKey* event)
2082 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2086 if (event->type != GDK_KEY_PRESS ||
2087 event->keyval == GDK_Page_Up ||
2088 event->keyval == GDK_Page_Down ||
2089 event->keyval == GDK_Up ||
2090 event->keyval == GDK_Down ||
2091 event->keyval == GDK_Left ||
2092 event->keyval == GDK_Right ||
2093 event->keyval == GDK_Shift_L ||
2094 event->keyval == GDK_Shift_R)
2097 /* turn off insert mode (if always_insert_mode is not used) */
2098 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2099 set_insert_mode(uzbl.behave.always_insert_mode);
2104 if (uzbl.behave.insert_mode &&
2105 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2106 (!uzbl.behave.modmask)
2111 if (event->keyval == GDK_Escape) {
2114 dehilight(uzbl.gui.web_view, NULL, NULL);
2118 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2119 if (event->keyval == GDK_Insert) {
2121 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2122 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2124 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2127 GString* keycmd = g_string_new(uzbl.state.keycmd);
2128 g_string_append (keycmd, str);
2129 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2136 if (event->keyval == GDK_BackSpace)
2137 keycmd_bs(NULL, NULL, NULL);
2139 gboolean key_ret = FALSE;
2140 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2143 GString* keycmd = g_string_new(uzbl.state.keycmd);
2144 g_string_append(keycmd, event->string);
2145 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2148 run_keycmd(key_ret);
2150 if (key_ret) return (!uzbl.behave.insert_mode);
2155 run_keycmd(const gboolean key_ret) {
2156 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2158 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2160 parse_command(act->name, act->param, NULL);
2164 /* try if it's an incremental keycmd or one that takes args, and run it */
2165 GString* short_keys = g_string_new ("");
2166 GString* short_keys_inc = g_string_new ("");
2168 guint len = strlen(uzbl.state.keycmd);
2169 for (i=0; i<len; i++) {
2170 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2171 g_string_assign(short_keys_inc, short_keys->str);
2172 g_string_append_c(short_keys, '_');
2173 g_string_append_c(short_keys_inc, '*');
2175 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2176 /* run normal cmds only if return was pressed */
2177 exec_paramcmd(act, i);
2180 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2181 if (key_ret) /* just quit the incremental command on return */
2183 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2187 g_string_truncate(short_keys, short_keys->len - 1);
2189 g_string_free (short_keys, TRUE);
2190 g_string_free (short_keys_inc, TRUE);
2194 exec_paramcmd(const Action *act, const guint i) {
2195 GString *parampart = g_string_new (uzbl.state.keycmd);
2196 GString *actionname = g_string_new ("");
2197 GString *actionparam = g_string_new ("");
2198 g_string_erase (parampart, 0, i+1);
2200 g_string_printf (actionname, act->name, parampart->str);
2202 g_string_printf (actionparam, act->param, parampart->str);
2203 parse_command(actionname->str, actionparam->str, NULL);
2204 g_string_free(actionname, TRUE);
2205 g_string_free(actionparam, TRUE);
2206 g_string_free(parampart, TRUE);
2214 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2216 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2217 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2218 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2219 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2220 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2221 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2222 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2223 g_signal_connect (G_OBJECT (g->web_view), "navigation-policy-decision-requested", G_CALLBACK (navigation_decision_cb), g->web_view);
2224 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2225 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2226 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2227 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2234 g->mainbar = gtk_hbox_new (FALSE, 0);
2236 /* keep a reference to the bar so we can re-pack it at runtime*/
2237 //sbar_ref = g_object_ref(g->mainbar);
2239 g->mainbar_label = gtk_label_new ("");
2240 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2241 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2242 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2243 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2244 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2245 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2251 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2252 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2253 gtk_widget_set_name (window, "Uzbl browser");
2254 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2255 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2256 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2263 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2264 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2265 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2272 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2274 If actname is one that calls an external command, this function will inject
2275 newargs in front of the user-provided args in that command line. They will
2276 come become after the body of the script (in sh) or after the name of
2277 the command to execute (in spawn).
2278 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2279 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2281 The return value consist of two strings: the action (sh, ...) and its args.
2283 If act is not one that calls an external command, then the given action merely
2286 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2287 /* Arrr! Here be memory leaks */
2288 gchar *actdup = g_strdup(actname);
2289 g_array_append_val(rets, actdup);
2291 if ((g_strcmp0(actname, "spawn") == 0) ||
2292 (g_strcmp0(actname, "sh") == 0) ||
2293 (g_strcmp0(actname, "sync_spawn") == 0) ||
2294 (g_strcmp0(actname, "sync_sh") == 0)) {
2296 GString *a = g_string_new("");
2297 gchar **spawnparts = split_quoted(origargs, FALSE);
2298 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2299 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2301 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2302 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2304 g_array_append_val(rets, a->str);
2305 g_string_free(a, FALSE);
2306 g_strfreev(spawnparts);
2308 gchar *origdup = g_strdup(origargs);
2309 g_array_append_val(rets, origdup);
2311 return (gchar**)g_array_free(rets, FALSE);
2315 run_handler (const gchar *act, const gchar *args) {
2316 /* Consider this code a temporary hack to make the handlers usable.
2317 In practice, all this splicing, injection, and reconstruction is
2318 inefficient, annoying and hard to manage. Potential pitfalls arise
2319 when the handler specific args 1) are not quoted (the handler
2320 callbacks should take care of this) 2) are quoted but interfere
2321 with the users' own quotation. A more ideal solution is
2322 to refactor parse_command so that it doesn't just take a string
2323 and execute it; rather than that, we should have a function which
2324 returns the argument vector parsed from the string. This vector
2325 could be modified (e.g. insert additional args into it) before
2326 passing it to the next function that actually executes it. Though
2327 it still isn't perfect for chain actions.. will reconsider & re-
2328 factor when I have the time. -duc */
2330 char **parts = g_strsplit(act, " ", 2);
2332 if (g_strcmp0(parts[0], "chain") == 0) {
2333 GString *newargs = g_string_new("");
2334 gchar **chainparts = split_quoted(parts[1], FALSE);
2336 /* for every argument in the chain, inject the handler args
2337 and make sure the new parts are wrapped in quotes */
2338 gchar **cp = chainparts;
2340 gchar *quotless = NULL;
2341 gchar **spliced_quotless = NULL; // sigh -_-;
2342 gchar **inpart = NULL;
2345 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2347 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2348 } else quotless = g_strdup(*cp);
2350 spliced_quotless = g_strsplit(quotless, " ", 2);
2351 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2352 g_strfreev(spliced_quotless);
2354 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2360 parse_command(parts[0], &(newargs->str[1]), NULL);
2361 g_string_free(newargs, TRUE);
2362 g_strfreev(chainparts);
2365 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2366 parse_command(inparts[0], inparts[1], NULL);
2374 add_binding (const gchar *key, const gchar *act) {
2375 char **parts = g_strsplit(act, " ", 2);
2382 if (uzbl.state.verbose)
2383 printf ("Binding %-10s : %s\n", key, act);
2384 action = new_action(parts[0], parts[1]);
2386 if (g_hash_table_remove (uzbl.bindings, key))
2387 g_warning ("Overwriting existing binding for \"%s\"", key);
2388 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2393 get_xdg_var (XDG_Var xdg) {
2394 const gchar* actual_value = getenv (xdg.environmental);
2395 const gchar* home = getenv ("HOME");
2396 gchar* return_value;
2398 if (! actual_value || strcmp (actual_value, "") == 0) {
2399 if (xdg.default_value) {
2400 return_value = str_replace ("~", home, xdg.default_value);
2402 return_value = NULL;
2405 return_value = str_replace("~", home, actual_value);
2408 return return_value;
2412 find_xdg_file (int xdg_type, char* filename) {
2413 /* xdg_type = 0 => config
2414 xdg_type = 1 => data
2415 xdg_type = 2 => cache*/
2417 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2418 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2421 gchar* temporary_string;
2425 if (! file_exists (temporary_file) && xdg_type != 2) {
2426 buf = get_xdg_var (XDG[3 + xdg_type]);
2427 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2430 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2431 g_free (temporary_file);
2432 temporary_file = g_strconcat (temporary_string, filename, NULL);
2436 //g_free (temporary_string); - segfaults.
2438 if (file_exists (temporary_file)) {
2439 return temporary_file;
2446 State *s = &uzbl.state;
2447 Network *n = &uzbl.net;
2449 for (i = 0; default_config[i].command != NULL; i++) {
2450 parse_cmd_line(default_config[i].command, NULL);
2453 if (g_strcmp0(s->config_file, "-") == 0) {
2454 s->config_file = NULL;
2458 else if (!s->config_file) {
2459 s->config_file = find_xdg_file (0, "/uzbl/config");
2462 if (s->config_file) {
2463 GArray* lines = read_file_by_line (s->config_file);
2467 while ((line = g_array_index(lines, gchar*, i))) {
2468 parse_cmd_line (line, NULL);
2472 g_array_free (lines, TRUE);
2474 if (uzbl.state.verbose)
2475 printf ("No configuration file loaded.\n");
2478 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2481 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2484 if (!uzbl.behave.cookie_handler)
2487 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2488 GString *s = g_string_new ("");
2489 SoupURI * soup_uri = soup_message_get_uri(msg);
2490 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2491 run_handler(uzbl.behave.cookie_handler, s->str);
2493 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2494 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2495 if ( p != NULL ) *p = '\0';
2496 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2498 if (uzbl.comm.sync_stdout)
2499 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2501 g_string_free(s, TRUE);
2505 save_cookies (SoupMessage *msg, gpointer user_data){
2509 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2510 cookie = soup_cookie_to_set_cookie_header(ck->data);
2511 SoupURI * soup_uri = soup_message_get_uri(msg);
2512 GString *s = g_string_new ("");
2513 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2514 run_handler(uzbl.behave.cookie_handler, s->str);
2516 g_string_free(s, TRUE);
2521 /* --- WEBINSPECTOR --- */
2523 hide_window_cb(GtkWidget *widget, gpointer data) {
2526 gtk_widget_hide(widget);
2530 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2533 (void) web_inspector;
2534 GtkWidget* scrolled_window;
2535 GtkWidget* new_web_view;
2538 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2539 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2540 G_CALLBACK(hide_window_cb), NULL);
2542 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2543 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2544 gtk_widget_show(g->inspector_window);
2546 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2547 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2548 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2549 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2550 gtk_widget_show(scrolled_window);
2552 new_web_view = webkit_web_view_new();
2553 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2555 return WEBKIT_WEB_VIEW(new_web_view);
2559 inspector_show_window_cb (WebKitWebInspector* inspector){
2561 gtk_widget_show(uzbl.gui.inspector_window);
2565 /* TODO: Add variables and code to make use of these functions */
2567 inspector_close_window_cb (WebKitWebInspector* inspector){
2573 inspector_attach_window_cb (WebKitWebInspector* inspector){
2579 inspector_detach_window_cb (WebKitWebInspector* inspector){
2585 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2591 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2597 set_up_inspector() {
2599 WebKitWebSettings *settings = view_settings();
2600 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2602 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2603 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2604 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2605 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2606 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2607 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2608 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2610 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2614 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2616 uzbl_cmdprop *c = v;
2621 if(c->type == TYPE_STR)
2622 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2623 else if(c->type == TYPE_INT)
2624 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2625 else if(c->type == TYPE_FLOAT)
2626 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2630 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2634 printf("bind %s = %s %s\n", (char *)k ,
2635 (char *)a->name, a->param?(char *)a->param:"");
2640 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2641 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2645 retreive_geometry() {
2647 GString *buf = g_string_new("");
2649 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2650 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2652 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2654 if(uzbl.gui.geometry)
2655 g_free(uzbl.gui.geometry);
2656 uzbl.gui.geometry = g_string_free(buf, FALSE);
2659 /* set up gtk, gobject, variable defaults and other things that tests and other
2660 * external applications need to do anyhow */
2662 initialize(int argc, char *argv[]) {
2663 if (!g_thread_supported ())
2664 g_thread_init (NULL);
2665 uzbl.state.executable_path = g_strdup(argv[0]);
2666 uzbl.state.selected_url = NULL;
2667 uzbl.state.searchtx = NULL;
2669 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2670 g_option_context_add_main_entries (context, entries, NULL);
2671 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2672 g_option_context_parse (context, &argc, &argv, NULL);
2673 g_option_context_free(context);
2675 if (uzbl.behave.print_version) {
2676 printf("Commit: %s\n", COMMIT);
2680 /* initialize hash table */
2681 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2683 uzbl.net.soup_session = webkit_get_default_session();
2684 uzbl.state.keycmd = g_strdup("");
2686 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2687 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2688 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2689 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2690 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2691 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2693 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2694 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2695 uzbl.gui.sbar.progress_w = 10;
2697 /* HTML mode defaults*/
2698 uzbl.behave.html_buffer = g_string_new("");
2699 uzbl.behave.html_endmarker = g_strdup(".");
2700 uzbl.behave.html_timeout = 60;
2701 uzbl.behave.base_url = g_strdup("http://invalid");
2703 /* default mode indicators */
2704 uzbl.behave.insert_indicator = g_strdup("I");
2705 uzbl.behave.cmd_indicator = g_strdup("C");
2707 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2708 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2709 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2710 uzbl.info.arch = ARCH;
2711 uzbl.info.commit = COMMIT;
2714 make_var_to_name_hash();
2719 #ifndef UZBL_LIBRARY
2722 main (int argc, char* argv[]) {
2723 initialize(argc, argv);
2725 gtk_init (&argc, &argv);
2727 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2728 //main_window_ref = g_object_ref(scrolled_window);
2729 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2730 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2732 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2733 GTK_WIDGET (uzbl.gui.web_view));
2735 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2739 /* initial packing */
2740 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2741 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2743 if (uzbl.state.socket_id) {
2744 uzbl.gui.plug = create_plug ();
2745 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2746 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2748 uzbl.gui.main_window = create_window ();
2749 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2750 gtk_widget_show_all (uzbl.gui.main_window);
2751 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2754 if(!uzbl.state.instance_name)
2755 uzbl.state.instance_name = itos((int)uzbl.xwin);
2757 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2759 if (uzbl.state.verbose) {
2760 printf("Uzbl start location: %s\n", argv[0]);
2761 if (uzbl.state.socket_id)
2762 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2764 printf("window_id %i\n",(int) uzbl.xwin);
2765 printf("pid %i\n", getpid ());
2766 printf("name: %s\n", uzbl.state.instance_name);
2767 printf("commit: %s\n", uzbl.info.commit);
2770 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2771 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2772 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2773 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2774 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2776 if(uzbl.gui.geometry)
2779 retreive_geometry();
2781 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2782 if (argc > 1 && !uzbl.state.uri)
2783 uri_override = g_strdup(argv[1]);
2784 gboolean verbose_override = uzbl.state.verbose;
2787 set_insert_mode(FALSE);
2789 if (!uzbl.behave.show_status)
2790 gtk_widget_hide(uzbl.gui.mainbar);
2797 if (verbose_override > uzbl.state.verbose)
2798 uzbl.state.verbose = verbose_override;
2801 set_var_value("uri", uri_override);
2802 g_free(uri_override);
2803 } else if (uzbl.state.uri)
2804 cmd_load_uri(uzbl.gui.web_view, NULL);
2809 return EXIT_SUCCESS;
2813 /* vi: set et ts=4: */