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 SoupURI *suri = soup_uri_new(uri);
532 gboolean decision_made = FALSE;
534 if (uzbl.state.verbose)
535 printf("Navigation requested -> %s\n", uri);
537 if (suri && strcmp (suri->scheme, "http") &&
538 strcmp (suri->scheme, "https") &&
539 strcmp (suri->scheme, "data") &&
540 strcmp (suri->scheme, "file") &&
541 strcmp (suri->scheme, "about")) {
542 if (uzbl.behave.scheme_handler) {
543 GString *s = g_string_new ("");
544 g_string_printf(s, "'%s'", uri);
546 run_handler(uzbl.behave.scheme_handler, s->str);
548 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
549 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
550 if ( p != NULL ) *p = '\0';
551 if (!strcmp(uzbl.comm.sync_stdout, "USED")) {
552 webkit_web_policy_decision_ignore(policy_decision);
553 decision_made = TRUE;
556 if (uzbl.comm.sync_stdout)
557 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
559 g_string_free(s, TRUE);
563 webkit_web_policy_decision_use(policy_decision);
569 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
572 (void) navigation_action;
573 (void) policy_decision;
575 const gchar* uri = webkit_network_request_get_uri (request);
576 if (uzbl.state.verbose)
577 printf("New window requested -> %s \n", uri);
578 webkit_web_policy_decision_use(policy_decision);
583 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
588 /* If we can display it, let's display it... */
589 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
590 webkit_web_policy_decision_use (policy_decision);
594 /* ...everything we can't displayed is downloaded */
595 webkit_web_policy_decision_download (policy_decision);
600 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
604 if (uzbl.state.selected_url != NULL) {
605 if (uzbl.state.verbose)
606 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
607 new_window_load_uri(uzbl.state.selected_url);
609 if (uzbl.state.verbose)
610 printf("New web view -> %s\n","Nothing to open, exiting");
616 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
619 if (uzbl.behave.download_handler) {
620 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
621 if (uzbl.state.verbose)
622 printf("Download -> %s\n",uri);
623 /* if urls not escaped, we may have to escape and quote uri before this call */
624 run_handler(uzbl.behave.download_handler, uri);
629 /* scroll a bar in a given direction */
631 scroll (GtkAdjustment* bar, GArray *argv) {
635 gdouble page_size = gtk_adjustment_get_page_size(bar);
636 gdouble value = gtk_adjustment_get_value(bar);
637 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
640 value += page_size * amount * 0.01;
644 max_value = gtk_adjustment_get_upper(bar) - page_size;
646 if (value > max_value)
647 value = max_value; /* don't scroll past the end of the page */
649 gtk_adjustment_set_value (bar, value);
653 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
654 (void) page; (void) argv; (void) result;
655 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
659 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
660 (void) page; (void) argv; (void) result;
661 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
662 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
666 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
667 (void) page; (void) result;
668 scroll(uzbl.gui.bar_v, argv);
672 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
673 (void) page; (void) result;
674 scroll(uzbl.gui.bar_h, argv);
679 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
680 if(uzbl.state.verbose)
681 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
683 /* update geometry var with the actual geometry
684 this is necessary as some WMs don't seem to honour
685 the above setting and we don't want to end up with
686 wrong geometry information
693 if (!uzbl.behave.show_status) {
694 gtk_widget_hide(uzbl.gui.mainbar);
696 gtk_widget_show(uzbl.gui.mainbar);
702 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
707 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
711 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
716 if (uzbl.behave.show_status) {
717 gtk_widget_hide(uzbl.gui.mainbar);
719 gtk_widget_show(uzbl.gui.mainbar);
721 uzbl.behave.show_status = !uzbl.behave.show_status;
726 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
730 //Set selected_url state variable
731 g_free(uzbl.state.selected_url);
732 uzbl.state.selected_url = NULL;
734 uzbl.state.selected_url = g_strdup(link);
740 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
743 const gchar *title = webkit_web_view_get_title(web_view);
744 if (uzbl.gui.main_title)
745 g_free (uzbl.gui.main_title);
746 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
751 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
754 uzbl.gui.sbar.load_progress = progress;
756 g_free(uzbl.gui.sbar.progress_bar);
757 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
763 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
767 if (uzbl.behave.load_finish_handler)
768 run_handler(uzbl.behave.load_finish_handler, "");
771 void clear_keycmd() {
772 g_free(uzbl.state.keycmd);
773 uzbl.state.keycmd = g_strdup("");
777 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
781 uzbl.gui.sbar.load_progress = 0;
782 clear_keycmd(); // don't need old commands to remain on new page?
783 if (uzbl.behave.load_start_handler)
784 run_handler(uzbl.behave.load_start_handler, "");
788 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
791 g_free (uzbl.state.uri);
792 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
793 uzbl.state.uri = g_string_free (newuri, FALSE);
794 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
795 set_insert_mode(uzbl.behave.always_insert_mode);
798 if (uzbl.behave.load_commit_handler)
799 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
803 destroy_cb (GtkWidget* widget, gpointer data) {
811 if (uzbl.behave.history_handler) {
813 struct tm * timeinfo;
816 timeinfo = localtime ( &rawtime );
817 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
818 run_handler(uzbl.behave.history_handler, date);
823 /* VIEW funcs (little webkit wrappers) */
824 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
826 VIEWFUNC(reload_bypass_cache)
827 VIEWFUNC(stop_loading)
834 /* -- command to callback/function map for things we cannot attach to any signals */
835 struct {char *key; CommandInfo value;} cmdlist[] =
836 { /* key function no_split */
837 { "back", {view_go_back, 0} },
838 { "forward", {view_go_forward, 0} },
839 { "scroll_vert", {scroll_vert, 0} },
840 { "scroll_horz", {scroll_horz, 0} },
841 { "scroll_begin", {scroll_begin, 0} },
842 { "scroll_end", {scroll_end, 0} },
843 { "reload", {view_reload, 0}, },
844 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
845 { "stop", {view_stop_loading, 0}, },
846 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
847 { "zoom_out", {view_zoom_out, 0}, },
848 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
849 { "uri", {load_uri, TRUE} },
850 { "js", {run_js, TRUE} },
851 { "script", {run_external_js, 0} },
852 { "toggle_status", {toggle_status_cb, 0} },
853 { "spawn", {spawn, 0} },
854 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
855 { "sh", {spawn_sh, 0} },
856 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
857 { "exit", {close_uzbl, 0} },
858 { "search", {search_forward_text, TRUE} },
859 { "search_reverse", {search_reverse_text, TRUE} },
860 { "dehilight", {dehilight, 0} },
861 { "toggle_insert_mode", {toggle_insert_mode, 0} },
862 { "set", {set_var, TRUE} },
863 //{ "get", {get_var, TRUE} },
864 { "bind", {act_bind, TRUE} },
865 { "dump_config", {act_dump_config, 0} },
866 { "keycmd", {keycmd, TRUE} },
867 { "keycmd_nl", {keycmd_nl, TRUE} },
868 { "keycmd_bs", {keycmd_bs, 0} },
869 { "chain", {chain, 0} },
870 { "print", {print, TRUE} }
877 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
879 for (i = 0; i < LENGTH(cmdlist); i++)
880 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
883 /* -- CORE FUNCTIONS -- */
886 free_action(gpointer act) {
887 Action *action = (Action*)act;
888 g_free(action->name);
890 g_free(action->param);
895 new_action(const gchar *name, const gchar *param) {
896 Action *action = g_new(Action, 1);
898 action->name = g_strdup(name);
900 action->param = g_strdup(param);
902 action->param = NULL;
908 file_exists (const char * filename) {
909 return (access(filename, F_OK) == 0);
913 set_var(WebKitWebView *page, GArray *argv, GString *result) {
914 (void) page; (void) result;
915 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
916 if (split[0] != NULL) {
917 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
918 set_var_value(g_strstrip(split[0]), value);
925 print(WebKitWebView *page, GArray *argv, GString *result) {
926 (void) page; (void) result;
929 buf = expand(argv_idx(argv, 0), 0);
930 g_string_assign(result, buf);
935 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
936 (void) page; (void) result;
937 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
938 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
939 add_binding(g_strstrip(split[0]), value);
957 set_mode_indicator() {
958 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
959 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
964 set_mode_indicator();
969 set_insert_mode(gboolean mode) {
970 uzbl.behave.insert_mode = mode;
971 set_mode_indicator();
975 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
976 (void) page; (void) result;
978 if (argv_idx(argv, 0)) {
979 if (strcmp (argv_idx(argv, 0), "0") == 0) {
980 set_insert_mode(FALSE);
982 set_insert_mode(TRUE);
985 set_insert_mode( !uzbl.behave.insert_mode );
992 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
995 if (argv_idx(argv, 0)) {
996 GString* newuri = g_string_new (argv_idx(argv, 0));
997 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
998 run_js(web_view, argv, NULL);
1001 if (!soup_uri_new(argv_idx(argv, 0)))
1002 g_string_prepend (newuri, "http://");
1003 /* if we do handle cookies, ask our handler for them */
1004 webkit_web_view_load_uri (web_view, newuri->str);
1005 g_string_free (newuri, TRUE);
1012 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
1013 size_t argumentCount, const JSValueRef arguments[],
1014 JSValueRef* exception) {
1019 JSStringRef js_result_string;
1020 GString *result = g_string_new("");
1022 if (argumentCount >= 1) {
1023 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
1024 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
1025 char ctl_line[arg_size];
1026 JSStringGetUTF8CString(arg, ctl_line, arg_size);
1028 parse_cmd_line(ctl_line, result);
1030 JSStringRelease(arg);
1032 js_result_string = JSStringCreateWithUTF8CString(result->str);
1034 g_string_free(result, TRUE);
1036 return JSValueMakeString(ctx, js_result_string);
1039 JSStaticFunction js_static_functions[] = {
1040 {"run", js_run_command, kJSPropertyAttributeNone},
1045 /* This function creates the class and its definition, only once */
1046 if (!uzbl.js.initialized) {
1047 /* it would be pretty cool to make this dynamic */
1048 uzbl.js.classdef = kJSClassDefinitionEmpty;
1049 uzbl.js.classdef.staticFunctions = js_static_functions;
1051 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1057 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1058 WebKitWebFrame *frame;
1059 JSGlobalContextRef context;
1060 JSObjectRef globalobject;
1061 JSStringRef var_name;
1063 JSStringRef js_script;
1064 JSValueRef js_result;
1065 JSStringRef js_result_string;
1066 size_t js_result_size;
1070 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1071 context = webkit_web_frame_get_global_context(frame);
1072 globalobject = JSContextGetGlobalObject(context);
1074 /* uzbl javascript namespace */
1075 var_name = JSStringCreateWithUTF8CString("Uzbl");
1076 JSObjectSetProperty(context, globalobject, var_name,
1077 JSObjectMake(context, uzbl.js.classref, NULL),
1078 kJSClassAttributeNone, NULL);
1080 /* evaluate the script and get return value*/
1081 js_script = JSStringCreateWithUTF8CString(script);
1082 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1083 if (js_result && !JSValueIsUndefined(context, js_result)) {
1084 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1085 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1087 if (js_result_size) {
1088 char js_result_utf8[js_result_size];
1089 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1090 g_string_assign(result, js_result_utf8);
1093 JSStringRelease(js_result_string);
1097 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1099 JSStringRelease(var_name);
1100 JSStringRelease(js_script);
1104 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1105 if (argv_idx(argv, 0))
1106 eval_js(web_view, argv_idx(argv, 0), result);
1110 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1112 if (argv_idx(argv, 0)) {
1113 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1118 while ((line = g_array_index(lines, gchar*, i))) {
1120 js = g_strdup (line);
1122 gchar* newjs = g_strconcat (js, line, NULL);
1129 if (uzbl.state.verbose)
1130 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1132 if (argv_idx (argv, 1)) {
1133 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1137 eval_js (web_view, js, result);
1139 g_array_free (lines, TRUE);
1144 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1145 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1146 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1147 webkit_web_view_unmark_text_matches (page);
1148 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1149 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1153 if (uzbl.state.searchtx) {
1154 if (uzbl.state.verbose)
1155 printf ("Searching: %s\n", uzbl.state.searchtx);
1156 webkit_web_view_set_highlight_text_matches (page, TRUE);
1157 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1162 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1164 search_text(page, argv, TRUE);
1168 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1170 search_text(page, argv, FALSE);
1174 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1175 (void) argv; (void) result;
1176 webkit_web_view_set_highlight_text_matches (page, FALSE);
1181 new_window_load_uri (const gchar * uri) {
1182 if (uzbl.behave.new_window) {
1183 GString *s = g_string_new ("");
1184 g_string_printf(s, "'%s'", uri);
1185 run_handler(uzbl.behave.new_window, s->str);
1188 GString* to_execute = g_string_new ("");
1189 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1191 for (i = 0; entries[i].long_name != NULL; i++) {
1192 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1193 gchar** str = (gchar**)entries[i].arg_data;
1195 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1199 if (uzbl.state.verbose)
1200 printf("\n%s\n", to_execute->str);
1201 g_spawn_command_line_async (to_execute->str, NULL);
1202 g_string_free (to_execute, TRUE);
1206 chain (WebKitWebView *page, GArray *argv, GString *result) {
1207 (void) page; (void) result;
1209 gchar **parts = NULL;
1211 while ((a = argv_idx(argv, i++))) {
1212 parts = g_strsplit (a, " ", 2);
1214 parse_command(parts[0], parts[1], result);
1220 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1224 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1230 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1234 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1240 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1245 int len = strlen(uzbl.state.keycmd);
1246 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1248 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1253 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1260 /* --Statusbar functions-- */
1262 build_progressbar_ascii(int percent) {
1263 int width=uzbl.gui.sbar.progress_w;
1266 GString *bar = g_string_new("");
1268 l = (double)percent*((double)width/100.);
1269 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1271 for(i=0; i<(int)l; i++)
1272 g_string_append(bar, uzbl.gui.sbar.progress_s);
1275 g_string_append(bar, uzbl.gui.sbar.progress_u);
1277 return g_string_free(bar, FALSE);
1279 /* --End Statusbar functions-- */
1282 sharg_append(GArray *a, const gchar *str) {
1283 const gchar *s = (str ? str : "");
1284 g_array_append_val(a, s);
1287 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1289 run_command (const gchar *command, const guint npre, const gchar **args,
1290 const gboolean sync, char **output_stdout) {
1291 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1294 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1295 gchar *pid = itos(getpid());
1296 gchar *xwin = itos(uzbl.xwin);
1298 sharg_append(a, command);
1299 for (i = 0; i < npre; i++) /* add n args before the default vars */
1300 sharg_append(a, args[i]);
1301 sharg_append(a, uzbl.state.config_file);
1302 sharg_append(a, pid);
1303 sharg_append(a, xwin);
1304 sharg_append(a, uzbl.comm.fifo_path);
1305 sharg_append(a, uzbl.comm.socket_path);
1306 sharg_append(a, uzbl.state.uri);
1307 sharg_append(a, uzbl.gui.main_title);
1309 for (i = npre; i < g_strv_length((gchar**)args); i++)
1310 sharg_append(a, args[i]);
1314 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1316 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1317 NULL, NULL, output_stdout, NULL, NULL, &err);
1318 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1319 NULL, NULL, NULL, &err);
1321 if (uzbl.state.verbose) {
1322 GString *s = g_string_new("spawned:");
1323 for (i = 0; i < (a->len); i++) {
1324 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1325 g_string_append_printf(s, " %s", qarg);
1328 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1329 printf("%s\n", s->str);
1330 g_string_free(s, TRUE);
1332 printf("Stdout: %s\n", *output_stdout);
1336 g_printerr("error on run_command: %s\n", err->message);
1341 g_array_free (a, TRUE);
1346 split_quoted(const gchar* src, const gboolean unquote) {
1347 /* split on unquoted space, return array of strings;
1348 remove a layer of quotes and backslashes if unquote */
1349 if (!src) return NULL;
1351 gboolean dq = FALSE;
1352 gboolean sq = FALSE;
1353 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1354 GString *s = g_string_new ("");
1358 for (p = src; *p != '\0'; p++) {
1359 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1360 else if (*p == '\\') { g_string_append_c(s, *p++);
1361 g_string_append_c(s, *p); }
1362 else if ((*p == '"') && unquote && !sq) dq = !dq;
1363 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1365 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1366 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1368 else if ((*p == ' ') && !dq && !sq) {
1369 dup = g_strdup(s->str);
1370 g_array_append_val(a, dup);
1371 g_string_truncate(s, 0);
1372 } else g_string_append_c(s, *p);
1374 dup = g_strdup(s->str);
1375 g_array_append_val(a, dup);
1376 ret = (gchar**)a->data;
1377 g_array_free (a, FALSE);
1378 g_string_free (s, TRUE);
1383 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1384 (void)web_view; (void)result;
1385 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1386 if (argv_idx(argv, 0))
1387 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1391 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1392 (void)web_view; (void)result;
1394 if (argv_idx(argv, 0))
1395 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1396 TRUE, &uzbl.comm.sync_stdout);
1400 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1401 (void)web_view; (void)result;
1402 if (!uzbl.behave.shell_cmd) {
1403 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1408 gchar *spacer = g_strdup("");
1409 g_array_insert_val(argv, 1, spacer);
1410 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1412 for (i = 1; i < g_strv_length(cmd); i++)
1413 g_array_prepend_val(argv, cmd[i]);
1415 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1421 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1422 (void)web_view; (void)result;
1423 if (!uzbl.behave.shell_cmd) {
1424 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1429 gchar *spacer = g_strdup("");
1430 g_array_insert_val(argv, 1, spacer);
1431 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1433 for (i = 1; i < g_strv_length(cmd); i++)
1434 g_array_prepend_val(argv, cmd[i]);
1436 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1437 TRUE, &uzbl.comm.sync_stdout);
1443 parse_command(const char *cmd, const char *param, GString *result) {
1446 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1448 gchar **par = split_quoted(param, TRUE);
1449 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1451 if (c->no_split) { /* don't split */
1452 sharg_append(a, param);
1454 for (i = 0; i < g_strv_length(par); i++)
1455 sharg_append(a, par[i]);
1458 if (result == NULL) {
1459 GString *result_print = g_string_new("");
1461 c->function(uzbl.gui.web_view, a, result_print);
1462 if (result_print->len)
1463 printf("%*s\n", result_print->len, result_print->str);
1465 g_string_free(result_print, TRUE);
1467 c->function(uzbl.gui.web_view, a, result);
1470 g_array_free (a, TRUE);
1473 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1480 if(*uzbl.net.proxy_url == ' '
1481 || uzbl.net.proxy_url == NULL) {
1482 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1483 (GType) SOUP_SESSION_PROXY_URI);
1486 suri = soup_uri_new(uzbl.net.proxy_url);
1487 g_object_set(G_OBJECT(uzbl.net.soup_session),
1488 SOUP_SESSION_PROXY_URI,
1490 soup_uri_free(suri);
1497 if(file_exists(uzbl.gui.icon)) {
1498 if (uzbl.gui.main_window)
1499 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1501 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1507 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1508 g_array_append_val (a, uzbl.state.uri);
1509 load_uri(uzbl.gui.web_view, a, NULL);
1510 g_array_free (a, TRUE);
1514 cmd_always_insert_mode() {
1515 set_insert_mode(uzbl.behave.always_insert_mode);
1521 g_object_set(G_OBJECT(uzbl.net.soup_session),
1522 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1526 cmd_max_conns_host() {
1527 g_object_set(G_OBJECT(uzbl.net.soup_session),
1528 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1533 soup_session_remove_feature
1534 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1535 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1536 /*g_free(uzbl.net.soup_logger);*/
1538 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1539 soup_session_add_feature(uzbl.net.soup_session,
1540 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1545 return webkit_web_view_get_settings(uzbl.gui.web_view);
1550 WebKitWebSettings *ws = view_settings();
1551 if (uzbl.behave.font_size > 0) {
1552 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1555 if (uzbl.behave.monospace_size > 0) {
1556 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1557 uzbl.behave.monospace_size, NULL);
1559 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1560 uzbl.behave.font_size, NULL);
1566 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1570 cmd_disable_plugins() {
1571 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1572 !uzbl.behave.disable_plugins, NULL);
1576 cmd_disable_scripts() {
1577 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1578 !uzbl.behave.disable_scripts, NULL);
1582 cmd_minimum_font_size() {
1583 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1584 uzbl.behave.minimum_font_size, NULL);
1587 cmd_autoload_img() {
1588 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1589 uzbl.behave.autoload_img, NULL);
1594 cmd_autoshrink_img() {
1595 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1596 uzbl.behave.autoshrink_img, NULL);
1601 cmd_enable_spellcheck() {
1602 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1603 uzbl.behave.enable_spellcheck, NULL);
1607 cmd_enable_private() {
1608 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1609 uzbl.behave.enable_private, NULL);
1614 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1615 uzbl.behave.print_bg, NULL);
1620 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1621 uzbl.behave.style_uri, NULL);
1625 cmd_resizable_txt() {
1626 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1627 uzbl.behave.resizable_txt, NULL);
1631 cmd_default_encoding() {
1632 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1633 uzbl.behave.default_encoding, NULL);
1637 cmd_enforce_96dpi() {
1638 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1639 uzbl.behave.enforce_96dpi, NULL);
1643 cmd_caret_browsing() {
1644 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1645 uzbl.behave.caret_browsing, NULL);
1649 cmd_cookie_handler() {
1650 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1651 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1652 if ((g_strcmp0(split[0], "sh") == 0) ||
1653 (g_strcmp0(split[0], "spawn") == 0)) {
1654 g_free (uzbl.behave.cookie_handler);
1655 uzbl.behave.cookie_handler =
1656 g_strdup_printf("sync_%s %s", split[0], split[1]);
1662 cmd_scheme_handler() {
1663 gchar **split = g_strsplit(uzbl.behave.scheme_handler, " ", 2);
1664 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1665 if ((g_strcmp0(split[0], "sh") == 0) ||
1666 (g_strcmp0(split[0], "spawn") == 0)) {
1667 g_free (uzbl.behave.scheme_handler);
1668 uzbl.behave.scheme_handler =
1669 g_strdup_printf("sync_%s %s", split[0], split[1]);
1676 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1681 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1686 if(uzbl.behave.inject_html) {
1687 webkit_web_view_load_html_string (uzbl.gui.web_view,
1688 uzbl.behave.inject_html, NULL);
1697 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1698 uzbl.behave.modmask = 0;
1700 if(uzbl.behave.modkey)
1701 g_free(uzbl.behave.modkey);
1702 uzbl.behave.modkey = buf;
1704 for (i = 0; modkeys[i].key != NULL; i++) {
1705 if (g_strrstr(buf, modkeys[i].key))
1706 uzbl.behave.modmask |= modkeys[i].mask;
1712 if (*uzbl.net.useragent == ' ') {
1713 g_free (uzbl.net.useragent);
1714 uzbl.net.useragent = NULL;
1716 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1717 uzbl.net.useragent, NULL);
1723 gtk_widget_ref(uzbl.gui.scrolled_win);
1724 gtk_widget_ref(uzbl.gui.mainbar);
1725 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1726 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1728 if(uzbl.behave.status_top) {
1729 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1730 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1733 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1734 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1736 gtk_widget_unref(uzbl.gui.scrolled_win);
1737 gtk_widget_unref(uzbl.gui.mainbar);
1738 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1743 set_var_value(gchar *name, gchar *val) {
1744 uzbl_cmdprop *c = NULL;
1748 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1749 if(!c->writeable) return FALSE;
1751 /* check for the variable type */
1752 if (c->type == TYPE_STR) {
1753 buf = expand(val, 0);
1756 } else if(c->type == TYPE_INT) {
1757 int *ip = (int *)c->ptr;
1758 buf = expand(val, 0);
1759 *ip = (int)strtoul(buf, &endp, 10);
1761 } else if (c->type == TYPE_FLOAT) {
1762 float *fp = (float *)c->ptr;
1763 buf = expand(val, 0);
1764 *fp = strtod(buf, &endp);
1768 /* invoke a command specific function */
1769 if(c->func) c->func();
1776 Behaviour *b = &uzbl.behave;
1778 if(b->html_buffer->str) {
1779 webkit_web_view_load_html_string (uzbl.gui.web_view,
1780 b->html_buffer->str, b->base_url);
1781 g_string_free(b->html_buffer, TRUE);
1782 b->html_buffer = g_string_new("");
1786 enum {M_CMD, M_HTML};
1788 parse_cmd_line(const char *ctl_line, GString *result) {
1789 Behaviour *b = &uzbl.behave;
1792 if(b->mode == M_HTML) {
1793 len = strlen(b->html_endmarker);
1794 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1795 if(len == strlen(ctl_line)-1 &&
1796 !strncmp(b->html_endmarker, ctl_line, len)) {
1798 set_var_value("mode", "0");
1803 set_timeout(b->html_timeout);
1804 g_string_append(b->html_buffer, ctl_line);
1807 else if((ctl_line[0] == '#') /* Comments */
1808 || (ctl_line[0] == ' ')
1809 || (ctl_line[0] == '\n'))
1810 ; /* ignore these lines */
1811 else { /* parse a command */
1813 gchar **tokens = NULL;
1814 len = strlen(ctl_line);
1816 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1817 ctlstrip = g_strndup(ctl_line, len - 1);
1818 else ctlstrip = g_strdup(ctl_line);
1820 tokens = g_strsplit(ctlstrip, " ", 2);
1821 parse_command(tokens[0], tokens[1], result);
1828 build_stream_name(int type, const gchar* dir) {
1829 State *s = &uzbl.state;
1833 str = g_strdup_printf
1834 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1835 } else if (type == SOCKET) {
1836 str = g_strdup_printf
1837 ("%s/uzbl_socket_%s", dir, s->instance_name);
1843 control_fifo(GIOChannel *gio, GIOCondition condition) {
1844 if (uzbl.state.verbose)
1845 printf("triggered\n");
1850 if (condition & G_IO_HUP)
1851 g_error ("Fifo: Read end of pipe died!\n");
1854 g_error ("Fifo: GIOChannel broke\n");
1856 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1857 if (ret == G_IO_STATUS_ERROR) {
1858 g_error ("Fifo: Error reading: %s\n", err->message);
1862 parse_cmd_line(ctl_line, NULL);
1869 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1870 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1871 if (unlink(uzbl.comm.fifo_path) == -1)
1872 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1873 g_free(uzbl.comm.fifo_path);
1874 uzbl.comm.fifo_path = NULL;
1877 GIOChannel *chan = NULL;
1878 GError *error = NULL;
1879 gchar *path = build_stream_name(FIFO, dir);
1881 if (!file_exists(path)) {
1882 if (mkfifo (path, 0666) == 0) {
1883 // 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.
1884 chan = g_io_channel_new_file(path, "r+", &error);
1886 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1887 if (uzbl.state.verbose)
1888 printf ("init_fifo: created successfully as %s\n", path);
1889 uzbl.comm.fifo_path = path;
1891 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1892 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1893 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1894 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1896 /* if we got this far, there was an error; cleanup */
1897 if (error) g_error_free (error);
1904 control_stdin(GIOChannel *gio, GIOCondition condition) {
1906 gchar *ctl_line = NULL;
1909 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1910 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1913 parse_cmd_line(ctl_line, NULL);
1921 GIOChannel *chan = NULL;
1922 GError *error = NULL;
1924 chan = g_io_channel_unix_new(fileno(stdin));
1926 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1927 g_error ("Stdin: could not add watch\n");
1929 if (uzbl.state.verbose)
1930 printf ("Stdin: watch added successfully\n");
1933 g_error ("Stdin: Error while opening: %s\n", error->message);
1935 if (error) g_error_free (error);
1939 control_socket(GIOChannel *chan) {
1940 struct sockaddr_un remote;
1941 unsigned int t = sizeof(remote);
1943 GIOChannel *clientchan;
1945 clientsock = accept (g_io_channel_unix_get_fd(chan),
1946 (struct sockaddr *) &remote, &t);
1948 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1949 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1950 (GIOFunc) control_client_socket, clientchan);
1957 control_client_socket(GIOChannel *clientchan) {
1959 GString *result = g_string_new("");
1960 GError *error = NULL;
1964 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1965 if (ret == G_IO_STATUS_ERROR) {
1966 g_warning ("Error reading: %s\n", error->message);
1967 g_io_channel_shutdown(clientchan, TRUE, &error);
1969 } else if (ret == G_IO_STATUS_EOF) {
1970 /* shutdown and remove channel watch from main loop */
1971 g_io_channel_shutdown(clientchan, TRUE, &error);
1976 parse_cmd_line (ctl_line, result);
1977 g_string_append_c(result, '\n');
1978 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1980 if (ret == G_IO_STATUS_ERROR) {
1981 g_warning ("Error writing: %s", error->message);
1983 g_io_channel_flush(clientchan, &error);
1986 if (error) g_error_free (error);
1987 g_string_free(result, TRUE);
1993 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1994 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1995 if (unlink(uzbl.comm.socket_path) == -1)
1996 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1997 g_free(uzbl.comm.socket_path);
1998 uzbl.comm.socket_path = NULL;
2006 GIOChannel *chan = NULL;
2008 struct sockaddr_un local;
2009 gchar *path = build_stream_name(SOCKET, dir);
2011 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2013 local.sun_family = AF_UNIX;
2014 strcpy (local.sun_path, path);
2015 unlink (local.sun_path);
2017 len = strlen (local.sun_path) + sizeof (local.sun_family);
2018 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2019 if (uzbl.state.verbose)
2020 printf ("init_socket: opened in %s\n", path);
2023 if( (chan = g_io_channel_unix_new(sock)) ) {
2024 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2025 uzbl.comm.socket_path = path;
2028 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2030 /* if we got this far, there was an error; cleanup */
2037 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2038 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2040 // this function may be called very early when the templates are not set (yet), hence the checks
2042 update_title (void) {
2043 Behaviour *b = &uzbl.behave;
2046 if (b->show_status) {
2047 if (b->title_format_short) {
2048 parsed = expand(b->title_format_short, 0);
2049 if (uzbl.gui.main_window)
2050 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2053 if (b->status_format) {
2054 parsed = expand(b->status_format, 0);
2055 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2058 if (b->status_background) {
2060 gdk_color_parse (b->status_background, &color);
2061 //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)
2062 if (uzbl.gui.main_window)
2063 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2064 else if (uzbl.gui.plug)
2065 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2068 if (b->title_format_long) {
2069 parsed = expand(b->title_format_long, 0);
2070 if (uzbl.gui.main_window)
2071 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2078 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2082 retreive_geometry();
2087 key_press_cb (GtkWidget* window, GdkEventKey* event)
2089 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2093 if (event->type != GDK_KEY_PRESS ||
2094 event->keyval == GDK_Page_Up ||
2095 event->keyval == GDK_Page_Down ||
2096 event->keyval == GDK_Up ||
2097 event->keyval == GDK_Down ||
2098 event->keyval == GDK_Left ||
2099 event->keyval == GDK_Right ||
2100 event->keyval == GDK_Shift_L ||
2101 event->keyval == GDK_Shift_R)
2104 /* turn off insert mode (if always_insert_mode is not used) */
2105 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2106 set_insert_mode(uzbl.behave.always_insert_mode);
2111 if (uzbl.behave.insert_mode &&
2112 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2113 (!uzbl.behave.modmask)
2118 if (event->keyval == GDK_Escape) {
2121 dehilight(uzbl.gui.web_view, NULL, NULL);
2125 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2126 if (event->keyval == GDK_Insert) {
2128 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2129 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2131 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2134 GString* keycmd = g_string_new(uzbl.state.keycmd);
2135 g_string_append (keycmd, str);
2136 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2143 if (event->keyval == GDK_BackSpace)
2144 keycmd_bs(NULL, NULL, NULL);
2146 gboolean key_ret = FALSE;
2147 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2150 GString* keycmd = g_string_new(uzbl.state.keycmd);
2151 g_string_append(keycmd, event->string);
2152 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2155 run_keycmd(key_ret);
2157 if (key_ret) return (!uzbl.behave.insert_mode);
2162 run_keycmd(const gboolean key_ret) {
2163 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2165 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2167 parse_command(act->name, act->param, NULL);
2171 /* try if it's an incremental keycmd or one that takes args, and run it */
2172 GString* short_keys = g_string_new ("");
2173 GString* short_keys_inc = g_string_new ("");
2175 guint len = strlen(uzbl.state.keycmd);
2176 for (i=0; i<len; i++) {
2177 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2178 g_string_assign(short_keys_inc, short_keys->str);
2179 g_string_append_c(short_keys, '_');
2180 g_string_append_c(short_keys_inc, '*');
2182 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2183 /* run normal cmds only if return was pressed */
2184 exec_paramcmd(act, i);
2187 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2188 if (key_ret) /* just quit the incremental command on return */
2190 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2194 g_string_truncate(short_keys, short_keys->len - 1);
2196 g_string_free (short_keys, TRUE);
2197 g_string_free (short_keys_inc, TRUE);
2201 exec_paramcmd(const Action *act, const guint i) {
2202 GString *parampart = g_string_new (uzbl.state.keycmd);
2203 GString *actionname = g_string_new ("");
2204 GString *actionparam = g_string_new ("");
2205 g_string_erase (parampart, 0, i+1);
2207 g_string_printf (actionname, act->name, parampart->str);
2209 g_string_printf (actionparam, act->param, parampart->str);
2210 parse_command(actionname->str, actionparam->str, NULL);
2211 g_string_free(actionname, TRUE);
2212 g_string_free(actionparam, TRUE);
2213 g_string_free(parampart, TRUE);
2221 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2223 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2224 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2225 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2226 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2227 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2228 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2229 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2230 g_signal_connect (G_OBJECT (g->web_view), "navigation-policy-decision-requested", G_CALLBACK (navigation_decision_cb), g->web_view);
2231 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2232 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2233 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2234 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2241 g->mainbar = gtk_hbox_new (FALSE, 0);
2243 /* keep a reference to the bar so we can re-pack it at runtime*/
2244 //sbar_ref = g_object_ref(g->mainbar);
2246 g->mainbar_label = gtk_label_new ("");
2247 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2248 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2249 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2250 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2251 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2252 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2258 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2259 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2260 gtk_widget_set_name (window, "Uzbl browser");
2261 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2262 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2263 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2270 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2271 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2272 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2279 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2281 If actname is one that calls an external command, this function will inject
2282 newargs in front of the user-provided args in that command line. They will
2283 come become after the body of the script (in sh) or after the name of
2284 the command to execute (in spawn).
2285 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2286 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2288 The return value consist of two strings: the action (sh, ...) and its args.
2290 If act is not one that calls an external command, then the given action merely
2293 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2294 /* Arrr! Here be memory leaks */
2295 gchar *actdup = g_strdup(actname);
2296 g_array_append_val(rets, actdup);
2298 if ((g_strcmp0(actname, "spawn") == 0) ||
2299 (g_strcmp0(actname, "sh") == 0) ||
2300 (g_strcmp0(actname, "sync_spawn") == 0) ||
2301 (g_strcmp0(actname, "sync_sh") == 0)) {
2303 GString *a = g_string_new("");
2304 gchar **spawnparts = split_quoted(origargs, FALSE);
2305 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2306 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2308 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2309 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2311 g_array_append_val(rets, a->str);
2312 g_string_free(a, FALSE);
2313 g_strfreev(spawnparts);
2315 gchar *origdup = g_strdup(origargs);
2316 g_array_append_val(rets, origdup);
2318 return (gchar**)g_array_free(rets, FALSE);
2322 run_handler (const gchar *act, const gchar *args) {
2323 /* Consider this code a temporary hack to make the handlers usable.
2324 In practice, all this splicing, injection, and reconstruction is
2325 inefficient, annoying and hard to manage. Potential pitfalls arise
2326 when the handler specific args 1) are not quoted (the handler
2327 callbacks should take care of this) 2) are quoted but interfere
2328 with the users' own quotation. A more ideal solution is
2329 to refactor parse_command so that it doesn't just take a string
2330 and execute it; rather than that, we should have a function which
2331 returns the argument vector parsed from the string. This vector
2332 could be modified (e.g. insert additional args into it) before
2333 passing it to the next function that actually executes it. Though
2334 it still isn't perfect for chain actions.. will reconsider & re-
2335 factor when I have the time. -duc */
2337 char **parts = g_strsplit(act, " ", 2);
2339 if (g_strcmp0(parts[0], "chain") == 0) {
2340 GString *newargs = g_string_new("");
2341 gchar **chainparts = split_quoted(parts[1], FALSE);
2343 /* for every argument in the chain, inject the handler args
2344 and make sure the new parts are wrapped in quotes */
2345 gchar **cp = chainparts;
2347 gchar *quotless = NULL;
2348 gchar **spliced_quotless = NULL; // sigh -_-;
2349 gchar **inpart = NULL;
2352 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2354 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2355 } else quotless = g_strdup(*cp);
2357 spliced_quotless = g_strsplit(quotless, " ", 2);
2358 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2359 g_strfreev(spliced_quotless);
2361 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2367 parse_command(parts[0], &(newargs->str[1]), NULL);
2368 g_string_free(newargs, TRUE);
2369 g_strfreev(chainparts);
2372 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2373 parse_command(inparts[0], inparts[1], NULL);
2381 add_binding (const gchar *key, const gchar *act) {
2382 char **parts = g_strsplit(act, " ", 2);
2389 if (uzbl.state.verbose)
2390 printf ("Binding %-10s : %s\n", key, act);
2391 action = new_action(parts[0], parts[1]);
2393 if (g_hash_table_remove (uzbl.bindings, key))
2394 g_warning ("Overwriting existing binding for \"%s\"", key);
2395 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2400 get_xdg_var (XDG_Var xdg) {
2401 const gchar* actual_value = getenv (xdg.environmental);
2402 const gchar* home = getenv ("HOME");
2403 gchar* return_value;
2405 if (! actual_value || strcmp (actual_value, "") == 0) {
2406 if (xdg.default_value) {
2407 return_value = str_replace ("~", home, xdg.default_value);
2409 return_value = NULL;
2412 return_value = str_replace("~", home, actual_value);
2415 return return_value;
2419 find_xdg_file (int xdg_type, char* filename) {
2420 /* xdg_type = 0 => config
2421 xdg_type = 1 => data
2422 xdg_type = 2 => cache*/
2424 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2425 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2428 gchar* temporary_string;
2432 if (! file_exists (temporary_file) && xdg_type != 2) {
2433 buf = get_xdg_var (XDG[3 + xdg_type]);
2434 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2437 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2438 g_free (temporary_file);
2439 temporary_file = g_strconcat (temporary_string, filename, NULL);
2443 //g_free (temporary_string); - segfaults.
2445 if (file_exists (temporary_file)) {
2446 return temporary_file;
2453 State *s = &uzbl.state;
2454 Network *n = &uzbl.net;
2456 for (i = 0; default_config[i].command != NULL; i++) {
2457 parse_cmd_line(default_config[i].command, NULL);
2460 if (g_strcmp0(s->config_file, "-") == 0) {
2461 s->config_file = NULL;
2465 else if (!s->config_file) {
2466 s->config_file = find_xdg_file (0, "/uzbl/config");
2469 if (s->config_file) {
2470 GArray* lines = read_file_by_line (s->config_file);
2474 while ((line = g_array_index(lines, gchar*, i))) {
2475 parse_cmd_line (line, NULL);
2479 g_array_free (lines, TRUE);
2481 if (uzbl.state.verbose)
2482 printf ("No configuration file loaded.\n");
2485 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2488 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2491 if (!uzbl.behave.cookie_handler)
2494 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2495 GString *s = g_string_new ("");
2496 SoupURI * soup_uri = soup_message_get_uri(msg);
2497 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2498 run_handler(uzbl.behave.cookie_handler, s->str);
2500 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2501 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2502 if ( p != NULL ) *p = '\0';
2503 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2505 if (uzbl.comm.sync_stdout)
2506 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2508 g_string_free(s, TRUE);
2512 save_cookies (SoupMessage *msg, gpointer user_data){
2516 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2517 cookie = soup_cookie_to_set_cookie_header(ck->data);
2518 SoupURI * soup_uri = soup_message_get_uri(msg);
2519 GString *s = g_string_new ("");
2520 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2521 run_handler(uzbl.behave.cookie_handler, s->str);
2523 g_string_free(s, TRUE);
2528 /* --- WEBINSPECTOR --- */
2530 hide_window_cb(GtkWidget *widget, gpointer data) {
2533 gtk_widget_hide(widget);
2537 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2540 (void) web_inspector;
2541 GtkWidget* scrolled_window;
2542 GtkWidget* new_web_view;
2545 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2546 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2547 G_CALLBACK(hide_window_cb), NULL);
2549 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2550 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2551 gtk_widget_show(g->inspector_window);
2553 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2554 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2555 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2556 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2557 gtk_widget_show(scrolled_window);
2559 new_web_view = webkit_web_view_new();
2560 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2562 return WEBKIT_WEB_VIEW(new_web_view);
2566 inspector_show_window_cb (WebKitWebInspector* inspector){
2568 gtk_widget_show(uzbl.gui.inspector_window);
2572 /* TODO: Add variables and code to make use of these functions */
2574 inspector_close_window_cb (WebKitWebInspector* inspector){
2580 inspector_attach_window_cb (WebKitWebInspector* inspector){
2586 inspector_detach_window_cb (WebKitWebInspector* inspector){
2592 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2598 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2604 set_up_inspector() {
2606 WebKitWebSettings *settings = view_settings();
2607 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2609 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2610 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2611 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2612 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2613 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2614 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2615 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2617 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2621 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2623 uzbl_cmdprop *c = v;
2628 if(c->type == TYPE_STR)
2629 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2630 else if(c->type == TYPE_INT)
2631 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2632 else if(c->type == TYPE_FLOAT)
2633 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2637 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2641 printf("bind %s = %s %s\n", (char *)k ,
2642 (char *)a->name, a->param?(char *)a->param:"");
2647 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2648 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2652 retreive_geometry() {
2654 GString *buf = g_string_new("");
2656 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2657 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2659 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2661 if(uzbl.gui.geometry)
2662 g_free(uzbl.gui.geometry);
2663 uzbl.gui.geometry = g_string_free(buf, FALSE);
2666 /* set up gtk, gobject, variable defaults and other things that tests and other
2667 * external applications need to do anyhow */
2669 initialize(int argc, char *argv[]) {
2670 if (!g_thread_supported ())
2671 g_thread_init (NULL);
2672 uzbl.state.executable_path = g_strdup(argv[0]);
2673 uzbl.state.selected_url = NULL;
2674 uzbl.state.searchtx = NULL;
2676 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2677 g_option_context_add_main_entries (context, entries, NULL);
2678 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2679 g_option_context_parse (context, &argc, &argv, NULL);
2680 g_option_context_free(context);
2682 if (uzbl.behave.print_version) {
2683 printf("Commit: %s\n", COMMIT);
2687 /* initialize hash table */
2688 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2690 uzbl.net.soup_session = webkit_get_default_session();
2691 uzbl.state.keycmd = g_strdup("");
2693 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2694 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2695 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2696 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2697 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2698 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2700 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2701 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2702 uzbl.gui.sbar.progress_w = 10;
2704 /* HTML mode defaults*/
2705 uzbl.behave.html_buffer = g_string_new("");
2706 uzbl.behave.html_endmarker = g_strdup(".");
2707 uzbl.behave.html_timeout = 60;
2708 uzbl.behave.base_url = g_strdup("http://invalid");
2710 /* default mode indicators */
2711 uzbl.behave.insert_indicator = g_strdup("I");
2712 uzbl.behave.cmd_indicator = g_strdup("C");
2714 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2715 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2716 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2717 uzbl.info.arch = ARCH;
2718 uzbl.info.commit = COMMIT;
2721 make_var_to_name_hash();
2726 #ifndef UZBL_LIBRARY
2729 main (int argc, char* argv[]) {
2730 initialize(argc, argv);
2732 gtk_init (&argc, &argv);
2734 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2735 //main_window_ref = g_object_ref(scrolled_window);
2736 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2737 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2739 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2740 GTK_WIDGET (uzbl.gui.web_view));
2742 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2746 /* initial packing */
2747 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2748 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2750 if (uzbl.state.socket_id) {
2751 uzbl.gui.plug = create_plug ();
2752 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2753 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2755 uzbl.gui.main_window = create_window ();
2756 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2757 gtk_widget_show_all (uzbl.gui.main_window);
2758 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2761 if(!uzbl.state.instance_name)
2762 uzbl.state.instance_name = itos((int)uzbl.xwin);
2764 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2766 if (uzbl.state.verbose) {
2767 printf("Uzbl start location: %s\n", argv[0]);
2768 if (uzbl.state.socket_id)
2769 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2771 printf("window_id %i\n",(int) uzbl.xwin);
2772 printf("pid %i\n", getpid ());
2773 printf("name: %s\n", uzbl.state.instance_name);
2774 printf("commit: %s\n", uzbl.info.commit);
2777 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2778 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2779 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2780 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2781 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2783 if(uzbl.gui.geometry)
2786 retreive_geometry();
2788 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2789 if (argc > 1 && !uzbl.state.uri)
2790 uri_override = g_strdup(argv[1]);
2791 gboolean verbose_override = uzbl.state.verbose;
2794 set_insert_mode(FALSE);
2796 if (!uzbl.behave.show_status)
2797 gtk_widget_hide(uzbl.gui.mainbar);
2804 if (verbose_override > uzbl.state.verbose)
2805 uzbl.state.verbose = verbose_override;
2808 set_var_value("uri", uri_override);
2809 g_free(uri_override);
2810 } else if (uzbl.state.uri)
2811 cmd_load_uri(uzbl.gui.web_view, NULL);
2816 return EXIT_SUCCESS;
2820 /* vi: set et ts=4: */