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>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
79 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
81 "Print the version and exit", NULL },
82 { NULL, 0, 0, 0, NULL, NULL, NULL }
85 /* associate command names to their properties */
86 typedef const struct {
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_title)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
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, cmd_new_window)},
138 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
139 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
140 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
141 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
142 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
143 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
144 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
145 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
147 /* exported WebKitWebSettings properties */
148 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
149 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
150 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
151 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
152 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
153 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
154 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
155 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
156 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
157 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
158 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
159 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
160 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
161 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
162 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
163 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
165 /* constants (not dumpable or writeable) */
166 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
167 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
168 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
169 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
170 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
171 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
172 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
173 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
174 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
175 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
176 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
178 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
179 }, *n2v_p = var_name_to_ptr;
186 { "SHIFT", GDK_SHIFT_MASK }, // shift
187 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
188 { "CONTROL", GDK_CONTROL_MASK }, // control
189 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
190 { "MOD2", GDK_MOD2_MASK }, // 5th mod
191 { "MOD3", GDK_MOD3_MASK }, // 6th mod
192 { "MOD4", GDK_MOD4_MASK }, // 7th mod
193 { "MOD5", GDK_MOD5_MASK }, // 8th mod
194 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
195 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
196 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
197 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
198 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
199 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
200 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
201 { "META", GDK_META_MASK }, // meta (since 2.10)
206 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
207 * 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 --- */
219 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
221 get_exp_type(gchar *s) {
225 else if(*(s+1) == '{')
226 return EXP_BRACED_VAR;
227 else if(*(s+1) == '<')
229 else if(*(s+1) == '[')
232 return EXP_SIMPLE_VAR;
238 * recurse == 1: don't expand '@(command)@'
239 * recurse == 2: don't expand '@<java script>@'
242 expand(char *s, guint recurse) {
246 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
251 gchar *cmd_stdout = NULL;
253 GString *buf = g_string_new("");
254 GString *js_ret = g_string_new("");
259 g_string_append_c(buf, *++s);
264 etype = get_exp_type(s);
269 vend = strpbrk(s, end_simple_var);
270 if(!vend) vend = strchr(s, '\0');
274 vend = strchr(s, upto);
275 if(!vend) vend = strchr(s, '\0');
279 strcpy(str_end, ")@");
281 vend = strstr(s, str_end);
282 if(!vend) vend = strchr(s, '\0');
286 strcpy(str_end, ">@");
288 vend = strstr(s, str_end);
289 if(!vend) vend = strchr(s, '\0');
293 strcpy(str_end, "]@");
295 vend = strstr(s, str_end);
296 if(!vend) vend = strchr(s, '\0');
301 strncpy(ret, s, vend-s);
305 if(etype == EXP_SIMPLE_VAR ||
306 etype == EXP_BRACED_VAR) {
307 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
308 if(c->type == TYPE_STR && *c->ptr != NULL) {
309 g_string_append(buf, (gchar *)*c->ptr);
310 } else if(c->type == TYPE_INT) {
311 char *b = itos((uintptr_t)*c->ptr);
312 g_string_append(buf, b);
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 gchar* 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 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527 (void) navigation_action;
528 (void) policy_decision;
530 const gchar* uri = webkit_network_request_get_uri (request);
531 if (uzbl.state.verbose)
532 printf("New window requested -> %s \n", uri);
533 webkit_web_policy_decision_use(policy_decision);
538 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
543 /* If we can display it, let's display it... */
544 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
545 webkit_web_policy_decision_use (policy_decision);
549 /* ...everything we can't displayed is downloaded */
550 webkit_web_policy_decision_download (policy_decision);
555 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
559 if (uzbl.state.selected_url != NULL) {
560 if (uzbl.state.verbose)
561 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
562 new_window_load_uri(uzbl.state.selected_url);
564 if (uzbl.state.verbose)
565 printf("New web view -> %s\n","Nothing to open, exiting");
571 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
574 if (uzbl.behave.download_handler) {
575 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
576 if (uzbl.state.verbose)
577 printf("Download -> %s\n",uri);
578 /* if urls not escaped, we may have to escape and quote uri before this call */
579 run_handler(uzbl.behave.download_handler, uri);
584 /* scroll a bar in a given direction */
586 scroll (GtkAdjustment* bar, GArray *argv) {
590 gdouble page_size = gtk_adjustment_get_page_size(bar);
591 gdouble value = gtk_adjustment_get_value(bar);
592 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
595 value += page_size * amount * 0.01;
599 max_value = gtk_adjustment_get_upper(bar) - page_size;
601 if (value > max_value)
602 value = max_value; /* don't scroll past the end of the page */
604 gtk_adjustment_set_value (bar, value);
608 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
609 (void) page; (void) argv; (void) result;
610 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
614 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
615 (void) page; (void) argv; (void) result;
616 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
617 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
621 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
622 (void) page; (void) result;
623 scroll(uzbl.gui.bar_v, argv);
627 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
628 (void) page; (void) result;
629 scroll(uzbl.gui.bar_h, argv);
634 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
635 if(uzbl.state.verbose)
636 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
638 /* update geometry var with the actual geometry
639 this is necessary as some WMs don't seem to honour
640 the above setting and we don't want to end up with
641 wrong geometry information
648 if (!uzbl.behave.show_status) {
649 gtk_widget_hide(uzbl.gui.mainbar);
651 gtk_widget_show(uzbl.gui.mainbar);
657 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
662 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
666 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
671 if (uzbl.behave.show_status) {
672 gtk_widget_hide(uzbl.gui.mainbar);
674 gtk_widget_show(uzbl.gui.mainbar);
676 uzbl.behave.show_status = !uzbl.behave.show_status;
681 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
685 //Set selected_url state variable
686 g_free(uzbl.state.selected_url);
687 uzbl.state.selected_url = NULL;
689 uzbl.state.selected_url = g_strdup(link);
695 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
698 const gchar *title = webkit_web_view_get_title(web_view);
699 if (uzbl.gui.main_title)
700 g_free (uzbl.gui.main_title);
701 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
706 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
709 uzbl.gui.sbar.load_progress = progress;
711 g_free(uzbl.gui.sbar.progress_bar);
712 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
718 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
722 if (uzbl.behave.load_finish_handler)
723 run_handler(uzbl.behave.load_finish_handler, "");
726 void clear_keycmd() {
727 g_free(uzbl.state.keycmd);
728 uzbl.state.keycmd = g_strdup("");
732 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
736 uzbl.gui.sbar.load_progress = 0;
737 clear_keycmd(); // don't need old commands to remain on new page?
738 if (uzbl.behave.load_start_handler)
739 run_handler(uzbl.behave.load_start_handler, "");
743 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
746 g_free (uzbl.state.uri);
747 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
748 uzbl.state.uri = g_string_free (newuri, FALSE);
749 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
750 set_insert_mode(uzbl.behave.always_insert_mode);
753 if (uzbl.behave.load_commit_handler)
754 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
758 destroy_cb (GtkWidget* widget, gpointer data) {
766 if (uzbl.behave.history_handler) {
768 struct tm * timeinfo;
771 timeinfo = localtime ( &rawtime );
772 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
773 run_handler(uzbl.behave.history_handler, date);
778 /* VIEW funcs (little webkit wrappers) */
779 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
781 VIEWFUNC(reload_bypass_cache)
782 VIEWFUNC(stop_loading)
789 /* -- command to callback/function map for things we cannot attach to any signals */
790 static struct {char *key; CommandInfo value;} cmdlist[] =
791 { /* key function no_split */
792 { "back", {view_go_back, 0} },
793 { "forward", {view_go_forward, 0} },
794 { "scroll_vert", {scroll_vert, 0} },
795 { "scroll_horz", {scroll_horz, 0} },
796 { "scroll_begin", {scroll_begin, 0} },
797 { "scroll_end", {scroll_end, 0} },
798 { "reload", {view_reload, 0}, },
799 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
800 { "stop", {view_stop_loading, 0}, },
801 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
802 { "zoom_out", {view_zoom_out, 0}, },
803 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
804 { "uri", {load_uri, TRUE} },
805 { "js", {run_js, TRUE} },
806 { "script", {run_external_js, 0} },
807 { "toggle_status", {toggle_status_cb, 0} },
808 { "spawn", {spawn, 0} },
809 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
810 { "sh", {spawn_sh, 0} },
811 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
812 { "exit", {close_uzbl, 0} },
813 { "search", {search_forward_text, TRUE} },
814 { "search_reverse", {search_reverse_text, TRUE} },
815 { "dehilight", {dehilight, 0} },
816 { "toggle_insert_mode", {toggle_insert_mode, 0} },
817 { "set", {set_var, TRUE} },
818 //{ "get", {get_var, TRUE} },
819 { "bind", {act_bind, TRUE} },
820 { "dump_config", {act_dump_config, 0} },
821 { "keycmd", {keycmd, TRUE} },
822 { "keycmd_nl", {keycmd_nl, TRUE} },
823 { "keycmd_bs", {keycmd_bs, 0} },
824 { "chain", {chain, 0} },
825 { "print", {print, TRUE} }
832 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
834 for (i = 0; i < LENGTH(cmdlist); i++)
835 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
838 /* -- CORE FUNCTIONS -- */
841 free_action(gpointer act) {
842 Action *action = (Action*)act;
843 g_free(action->name);
845 g_free(action->param);
850 new_action(const gchar *name, const gchar *param) {
851 Action *action = g_new(Action, 1);
853 action->name = g_strdup(name);
855 action->param = g_strdup(param);
857 action->param = NULL;
863 file_exists (const char * filename) {
864 return (access(filename, F_OK) == 0);
868 set_var(WebKitWebView *page, GArray *argv, GString *result) {
869 (void) page; (void) result;
870 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
871 if (split[0] != NULL) {
872 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
873 set_var_value(g_strstrip(split[0]), value);
880 print(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) result;
884 buf = expand(argv_idx(argv, 0), 0);
885 g_string_assign(result, buf);
890 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
891 (void) page; (void) result;
892 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
893 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
894 add_binding(g_strstrip(split[0]), value);
910 void set_mode_indicator() {
911 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
912 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
915 void set_insert_mode(gboolean mode) {
916 uzbl.behave.insert_mode = mode;
917 set_mode_indicator();
921 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
922 (void) page; (void) result;
924 if (argv_idx(argv, 0)) {
925 if (strcmp (argv_idx(argv, 0), "0") == 0) {
926 set_insert_mode(FALSE);
928 set_insert_mode(TRUE);
931 set_insert_mode( !uzbl.behave.insert_mode );
938 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
941 if (argv_idx(argv, 0)) {
942 GString* newuri = g_string_new (argv_idx(argv, 0));
943 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
944 run_js(web_view, argv, NULL);
947 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
948 g_string_prepend (newuri, "http://");
949 /* if we do handle cookies, ask our handler for them */
950 webkit_web_view_load_uri (web_view, newuri->str);
951 g_string_free (newuri, TRUE);
959 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
960 size_t argumentCount, const JSValueRef arguments[],
961 JSValueRef* exception) {
966 JSStringRef js_result_string;
967 GString *result = g_string_new("");
969 if (argumentCount >= 1) {
970 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
971 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
972 char ctl_line[arg_size];
973 JSStringGetUTF8CString(arg, ctl_line, arg_size);
975 parse_cmd_line(ctl_line, result);
977 JSStringRelease(arg);
979 js_result_string = JSStringCreateWithUTF8CString(result->str);
981 g_string_free(result, TRUE);
983 return JSValueMakeString(ctx, js_result_string);
986 static JSStaticFunction js_static_functions[] = {
987 {"run", js_run_command, kJSPropertyAttributeNone},
992 /* This function creates the class and its definition, only once */
993 if (!uzbl.js.initialized) {
994 /* it would be pretty cool to make this dynamic */
995 uzbl.js.classdef = kJSClassDefinitionEmpty;
996 uzbl.js.classdef.staticFunctions = js_static_functions;
998 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1004 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1005 WebKitWebFrame *frame;
1006 JSGlobalContextRef context;
1007 JSObjectRef globalobject;
1008 JSStringRef var_name;
1010 JSStringRef js_script;
1011 JSValueRef js_result;
1012 JSStringRef js_result_string;
1013 size_t js_result_size;
1017 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1018 context = webkit_web_frame_get_global_context(frame);
1019 globalobject = JSContextGetGlobalObject(context);
1021 /* uzbl javascript namespace */
1022 var_name = JSStringCreateWithUTF8CString("Uzbl");
1023 JSObjectSetProperty(context, globalobject, var_name,
1024 JSObjectMake(context, uzbl.js.classref, NULL),
1025 kJSClassAttributeNone, NULL);
1027 /* evaluate the script and get return value*/
1028 js_script = JSStringCreateWithUTF8CString(script);
1029 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1030 if (js_result && !JSValueIsUndefined(context, js_result)) {
1031 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1032 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1034 if (js_result_size) {
1035 char js_result_utf8[js_result_size];
1036 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1037 g_string_assign(result, js_result_utf8);
1040 JSStringRelease(js_result_string);
1044 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1046 JSStringRelease(var_name);
1047 JSStringRelease(js_script);
1051 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1053 if (argv_idx(argv, 0))
1054 eval_js(web_view, argv_idx(argv, 0), result);
1058 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1060 if (argv_idx(argv, 0)) {
1061 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1066 while ((line = g_array_index(lines, gchar*, i))) {
1068 js = g_strdup (line);
1070 gchar* newjs = g_strconcat (js, line, NULL);
1077 if (uzbl.state.verbose)
1078 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1080 if (argv_idx (argv, 1)) {
1081 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1085 eval_js (web_view, js, result);
1087 g_array_free (lines, TRUE);
1092 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1093 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1094 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1095 webkit_web_view_unmark_text_matches (page);
1096 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1097 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1101 if (uzbl.state.searchtx) {
1102 if (uzbl.state.verbose)
1103 printf ("Searching: %s\n", uzbl.state.searchtx);
1104 webkit_web_view_set_highlight_text_matches (page, TRUE);
1105 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1110 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1112 search_text(page, argv, TRUE);
1116 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1118 search_text(page, argv, FALSE);
1122 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1123 (void) argv; (void) result;
1124 webkit_web_view_set_highlight_text_matches (page, FALSE);
1129 new_window_load_uri (const gchar * uri) {
1130 if (uzbl.behave.new_window) {
1131 GString *s = g_string_new ("");
1132 g_string_printf(s, "'%s'", uri);
1133 run_handler(uzbl.behave.new_window, s->str);
1136 GString* to_execute = g_string_new ("");
1137 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1139 for (i = 0; entries[i].long_name != NULL; i++) {
1140 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1141 gchar** str = (gchar**)entries[i].arg_data;
1143 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1147 if (uzbl.state.verbose)
1148 printf("\n%s\n", to_execute->str);
1149 g_spawn_command_line_async (to_execute->str, NULL);
1150 g_string_free (to_execute, TRUE);
1154 chain (WebKitWebView *page, GArray *argv, GString *result) {
1155 (void) page; (void) result;
1157 gchar **parts = NULL;
1159 while ((a = argv_idx(argv, i++))) {
1160 parts = g_strsplit (a, " ", 2);
1161 parse_command(parts[0], parts[1], result);
1167 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1171 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1177 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1181 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1187 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1192 int len = strlen(uzbl.state.keycmd);
1193 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1195 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1200 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1207 /* --Statusbar functions-- */
1209 build_progressbar_ascii(int percent) {
1210 int width=uzbl.gui.sbar.progress_w;
1213 GString *bar = g_string_new("");
1215 l = (double)percent*((double)width/100.);
1216 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1218 for(i=0; i<(int)l; i++)
1219 g_string_append(bar, uzbl.gui.sbar.progress_s);
1222 g_string_append(bar, uzbl.gui.sbar.progress_u);
1224 return g_string_free(bar, FALSE);
1226 /* --End Statusbar functions-- */
1229 sharg_append(GArray *a, const gchar *str) {
1230 const gchar *s = (str ? str : "");
1231 g_array_append_val(a, s);
1234 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1236 run_command (const gchar *command, const guint npre, const gchar **args,
1237 const gboolean sync, char **output_stdout) {
1238 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1241 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1242 gchar *pid = itos(getpid());
1243 gchar *xwin = itos(uzbl.xwin);
1245 sharg_append(a, command);
1246 for (i = 0; i < npre; i++) /* add n args before the default vars */
1247 sharg_append(a, args[i]);
1248 sharg_append(a, uzbl.state.config_file);
1249 sharg_append(a, pid);
1250 sharg_append(a, xwin);
1251 sharg_append(a, uzbl.comm.fifo_path);
1252 sharg_append(a, uzbl.comm.socket_path);
1253 sharg_append(a, uzbl.state.uri);
1254 sharg_append(a, uzbl.gui.main_title);
1256 for (i = npre; i < g_strv_length((gchar**)args); i++)
1257 sharg_append(a, args[i]);
1261 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1263 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1264 NULL, NULL, output_stdout, NULL, NULL, &err);
1265 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1266 NULL, NULL, NULL, &err);
1268 if (uzbl.state.verbose) {
1269 GString *s = g_string_new("spawned:");
1270 for (i = 0; i < (a->len); i++) {
1271 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1272 g_string_append_printf(s, " %s", qarg);
1275 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1276 printf("%s\n", s->str);
1277 g_string_free(s, TRUE);
1279 printf("Stdout: %s\n", *output_stdout);
1283 g_printerr("error on run_command: %s\n", err->message);
1288 g_array_free (a, TRUE);
1293 split_quoted(const gchar* src, const gboolean unquote) {
1294 /* split on unquoted space, return array of strings;
1295 remove a layer of quotes and backslashes if unquote */
1296 if (!src) return NULL;
1298 gboolean dq = FALSE;
1299 gboolean sq = FALSE;
1300 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1301 GString *s = g_string_new ("");
1305 for (p = src; *p != '\0'; p++) {
1306 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1307 else if (*p == '\\') { g_string_append_c(s, *p++);
1308 g_string_append_c(s, *p); }
1309 else if ((*p == '"') && unquote && !sq) dq = !dq;
1310 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1312 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1313 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1315 else if ((*p == ' ') && !dq && !sq) {
1316 dup = g_strdup(s->str);
1317 g_array_append_val(a, dup);
1318 g_string_truncate(s, 0);
1319 } else g_string_append_c(s, *p);
1321 dup = g_strdup(s->str);
1322 g_array_append_val(a, dup);
1323 ret = (gchar**)a->data;
1324 g_array_free (a, FALSE);
1325 g_string_free (s, TRUE);
1330 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1331 (void)web_view; (void)result;
1332 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1333 if (argv_idx(argv, 0))
1334 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1338 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1339 (void)web_view; (void)result;
1341 if (argv_idx(argv, 0))
1342 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1343 TRUE, &uzbl.comm.sync_stdout);
1347 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1348 (void)web_view; (void)result;
1349 if (!uzbl.behave.shell_cmd) {
1350 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1355 gchar *spacer = g_strdup("");
1356 g_array_insert_val(argv, 1, spacer);
1357 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1359 for (i = 1; i < g_strv_length(cmd); i++)
1360 g_array_prepend_val(argv, cmd[i]);
1362 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1368 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1369 (void)web_view; (void)result;
1370 if (!uzbl.behave.shell_cmd) {
1371 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1376 gchar *spacer = g_strdup("");
1377 g_array_insert_val(argv, 1, spacer);
1378 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1380 for (i = 1; i < g_strv_length(cmd); i++)
1381 g_array_prepend_val(argv, cmd[i]);
1383 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1384 TRUE, &uzbl.comm.sync_stdout);
1390 parse_command(const char *cmd, const char *param, GString *result) {
1393 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1395 gchar **par = split_quoted(param, TRUE);
1396 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1398 if (c->no_split) { /* don't split */
1399 sharg_append(a, param);
1401 for (i = 0; i < g_strv_length(par); i++)
1402 sharg_append(a, par[i]);
1405 if (result == NULL) {
1406 GString *result_print = g_string_new("");
1408 c->function(uzbl.gui.web_view, a, result_print);
1409 if (result_print->len)
1410 printf("%*s\n", result_print->len, result_print->str);
1412 g_string_free(result_print, TRUE);
1414 c->function(uzbl.gui.web_view, a, result);
1417 g_array_free (a, TRUE);
1420 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1427 if(*uzbl.net.proxy_url == ' '
1428 || uzbl.net.proxy_url == NULL) {
1429 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1430 (GType) SOUP_SESSION_PROXY_URI);
1433 suri = soup_uri_new(uzbl.net.proxy_url);
1434 g_object_set(G_OBJECT(uzbl.net.soup_session),
1435 SOUP_SESSION_PROXY_URI,
1437 soup_uri_free(suri);
1444 if(file_exists(uzbl.gui.icon)) {
1445 if (uzbl.gui.main_window)
1446 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1448 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1454 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1455 g_array_append_val (a, uzbl.state.uri);
1456 load_uri(uzbl.gui.web_view, a, NULL);
1457 g_array_free (a, TRUE);
1461 cmd_always_insert_mode() {
1462 set_insert_mode(uzbl.behave.always_insert_mode);
1468 g_object_set(G_OBJECT(uzbl.net.soup_session),
1469 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1473 cmd_max_conns_host() {
1474 g_object_set(G_OBJECT(uzbl.net.soup_session),
1475 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1480 soup_session_remove_feature
1481 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1482 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1483 /*g_free(uzbl.net.soup_logger);*/
1485 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1486 soup_session_add_feature(uzbl.net.soup_session,
1487 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1490 static WebKitWebSettings*
1492 return webkit_web_view_get_settings(uzbl.gui.web_view);
1497 WebKitWebSettings *ws = view_settings();
1498 if (uzbl.behave.font_size > 0) {
1499 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1502 if (uzbl.behave.monospace_size > 0) {
1503 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1504 uzbl.behave.monospace_size, NULL);
1506 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1507 uzbl.behave.font_size, NULL);
1513 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1517 cmd_disable_plugins() {
1518 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1519 !uzbl.behave.disable_plugins, NULL);
1523 cmd_disable_scripts() {
1524 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1525 !uzbl.behave.disable_scripts, NULL);
1529 cmd_minimum_font_size() {
1530 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1531 uzbl.behave.minimum_font_size, NULL);
1534 cmd_autoload_img() {
1535 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1536 uzbl.behave.autoload_img, NULL);
1541 cmd_autoshrink_img() {
1542 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1543 uzbl.behave.autoshrink_img, NULL);
1548 cmd_enable_spellcheck() {
1549 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1550 uzbl.behave.enable_spellcheck, NULL);
1554 cmd_enable_private() {
1555 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1556 uzbl.behave.enable_private, NULL);
1561 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1562 uzbl.behave.print_bg, NULL);
1567 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1568 uzbl.behave.style_uri, NULL);
1572 cmd_resizable_txt() {
1573 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1574 uzbl.behave.resizable_txt, NULL);
1578 cmd_default_encoding() {
1579 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1580 uzbl.behave.default_encoding, NULL);
1584 cmd_enforce_96dpi() {
1585 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1586 uzbl.behave.enforce_96dpi, NULL);
1590 cmd_caret_browsing() {
1591 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1592 uzbl.behave.caret_browsing, NULL);
1596 cmd_cookie_handler() {
1597 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1598 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1599 if ((g_strcmp0(split[0], "sh") == 0) ||
1600 (g_strcmp0(split[0], "spawn") == 0)) {
1601 g_free (uzbl.behave.cookie_handler);
1602 uzbl.behave.cookie_handler =
1603 g_strdup_printf("sync_%s %s", split[0], split[1]);
1610 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1611 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1612 if ((g_strcmp0(split[0], "sh") == 0) ||
1613 (g_strcmp0(split[0], "spawn") == 0)) {
1614 g_free (uzbl.behave.new_window);
1615 uzbl.behave.new_window =
1616 g_strdup_printf("%s %s", split[0], split[1]);
1623 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1628 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1633 if(uzbl.behave.inject_html) {
1634 webkit_web_view_load_html_string (uzbl.gui.web_view,
1635 uzbl.behave.inject_html, NULL);
1644 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1645 uzbl.behave.modmask = 0;
1647 if(uzbl.behave.modkey)
1648 g_free(uzbl.behave.modkey);
1649 uzbl.behave.modkey = buf;
1651 for (i = 0; modkeys[i].key != NULL; i++) {
1652 if (g_strrstr(buf, modkeys[i].key))
1653 uzbl.behave.modmask |= modkeys[i].mask;
1659 if (*uzbl.net.useragent == ' ') {
1660 g_free (uzbl.net.useragent);
1661 uzbl.net.useragent = NULL;
1663 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1664 uzbl.net.useragent, NULL);
1670 gtk_widget_ref(uzbl.gui.scrolled_win);
1671 gtk_widget_ref(uzbl.gui.mainbar);
1672 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1673 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1675 if(uzbl.behave.status_top) {
1676 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1677 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1680 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1681 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1683 gtk_widget_unref(uzbl.gui.scrolled_win);
1684 gtk_widget_unref(uzbl.gui.mainbar);
1685 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1690 set_var_value(gchar *name, gchar *val) {
1691 uzbl_cmdprop *c = NULL;
1695 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1696 if(!c->writeable) return TRUE;
1698 /* check for the variable type */
1699 if (c->type == TYPE_STR) {
1700 buf = expand(val, 0);
1703 } else if(c->type == TYPE_INT) {
1704 int *ip = (int *)c->ptr;
1705 buf = expand(val, 0);
1706 *ip = (int)strtoul(buf, &endp, 10);
1708 } else if (c->type == TYPE_FLOAT) {
1709 float *fp = (float *)c->ptr;
1710 buf = expand(val, 0);
1711 *fp = strtod(buf, &endp);
1715 /* invoke a command specific function */
1716 if(c->func) c->func();
1723 Behaviour *b = &uzbl.behave;
1725 if(b->html_buffer->str) {
1726 webkit_web_view_load_html_string (uzbl.gui.web_view,
1727 b->html_buffer->str, b->base_url);
1728 g_string_free(b->html_buffer, TRUE);
1729 b->html_buffer = g_string_new("");
1733 enum {M_CMD, M_HTML};
1735 parse_cmd_line(const char *ctl_line, GString *result) {
1736 Behaviour *b = &uzbl.behave;
1739 if(b->mode == M_HTML) {
1740 len = strlen(b->html_endmarker);
1741 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1742 if(len == strlen(ctl_line)-1 &&
1743 !strncmp(b->html_endmarker, ctl_line, len)) {
1745 set_var_value("mode", "0");
1750 set_timeout(b->html_timeout);
1751 g_string_append(b->html_buffer, ctl_line);
1754 else if((ctl_line[0] == '#') /* Comments */
1755 || (ctl_line[0] == ' ')
1756 || (ctl_line[0] == '\n'))
1757 ; /* ignore these lines */
1758 else { /* parse a command */
1760 gchar **tokens = NULL;
1761 len = strlen(ctl_line);
1763 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1764 ctlstrip = g_strndup(ctl_line, len - 1);
1765 else ctlstrip = g_strdup(ctl_line);
1767 tokens = g_strsplit(ctlstrip, " ", 2);
1768 parse_command(tokens[0], tokens[1], result);
1775 build_stream_name(int type, const gchar* dir) {
1776 State *s = &uzbl.state;
1780 str = g_strdup_printf
1781 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1782 } else if (type == SOCKET) {
1783 str = g_strdup_printf
1784 ("%s/uzbl_socket_%s", dir, s->instance_name);
1790 control_fifo(GIOChannel *gio, GIOCondition condition) {
1791 if (uzbl.state.verbose)
1792 printf("triggered\n");
1797 if (condition & G_IO_HUP)
1798 g_error ("Fifo: Read end of pipe died!\n");
1801 g_error ("Fifo: GIOChannel broke\n");
1803 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1804 if (ret == G_IO_STATUS_ERROR) {
1805 g_error ("Fifo: Error reading: %s\n", err->message);
1809 parse_cmd_line(ctl_line, NULL);
1816 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1817 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1818 if (unlink(uzbl.comm.fifo_path) == -1)
1819 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1820 g_free(uzbl.comm.fifo_path);
1821 uzbl.comm.fifo_path = NULL;
1824 if (*dir == ' ') { /* space unsets the variable */
1829 GIOChannel *chan = NULL;
1830 GError *error = NULL;
1831 gchar *path = build_stream_name(FIFO, dir);
1833 if (!file_exists(path)) {
1834 if (mkfifo (path, 0666) == 0) {
1835 // 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.
1836 chan = g_io_channel_new_file(path, "r+", &error);
1838 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1839 if (uzbl.state.verbose)
1840 printf ("init_fifo: created successfully as %s\n", path);
1841 uzbl.comm.fifo_path = path;
1843 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1844 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1845 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1846 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1848 /* if we got this far, there was an error; cleanup */
1849 if (error) g_error_free (error);
1856 control_stdin(GIOChannel *gio, GIOCondition condition) {
1858 gchar *ctl_line = NULL;
1861 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1862 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1865 parse_cmd_line(ctl_line, NULL);
1873 GIOChannel *chan = NULL;
1874 GError *error = NULL;
1876 chan = g_io_channel_unix_new(fileno(stdin));
1878 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1879 g_error ("Stdin: could not add watch\n");
1881 if (uzbl.state.verbose)
1882 printf ("Stdin: watch added successfully\n");
1885 g_error ("Stdin: Error while opening: %s\n", error->message);
1887 if (error) g_error_free (error);
1891 control_socket(GIOChannel *chan) {
1892 struct sockaddr_un remote;
1893 unsigned int t = sizeof(remote);
1895 GIOChannel *clientchan;
1897 clientsock = accept (g_io_channel_unix_get_fd(chan),
1898 (struct sockaddr *) &remote, &t);
1900 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1901 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1902 (GIOFunc) control_client_socket, clientchan);
1909 control_client_socket(GIOChannel *clientchan) {
1911 GString *result = g_string_new("");
1912 GError *error = NULL;
1916 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1917 if (ret == G_IO_STATUS_ERROR) {
1918 g_warning ("Error reading: %s\n", error->message);
1919 g_io_channel_shutdown(clientchan, TRUE, &error);
1921 } else if (ret == G_IO_STATUS_EOF) {
1922 /* shutdown and remove channel watch from main loop */
1923 g_io_channel_shutdown(clientchan, TRUE, &error);
1928 parse_cmd_line (ctl_line, result);
1929 g_string_append_c(result, '\n');
1930 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1932 if (ret == G_IO_STATUS_ERROR) {
1933 g_warning ("Error writing: %s", error->message);
1935 g_io_channel_flush(clientchan, &error);
1938 if (error) g_error_free (error);
1939 g_string_free(result, TRUE);
1945 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1946 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1947 if (unlink(uzbl.comm.socket_path) == -1)
1948 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1949 g_free(uzbl.comm.socket_path);
1950 uzbl.comm.socket_path = NULL;
1958 GIOChannel *chan = NULL;
1960 struct sockaddr_un local;
1961 gchar *path = build_stream_name(SOCKET, dir);
1963 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1965 local.sun_family = AF_UNIX;
1966 strcpy (local.sun_path, path);
1967 unlink (local.sun_path);
1969 len = strlen (local.sun_path) + sizeof (local.sun_family);
1970 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1971 if (uzbl.state.verbose)
1972 printf ("init_socket: opened in %s\n", path);
1975 if( (chan = g_io_channel_unix_new(sock)) ) {
1976 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1977 uzbl.comm.socket_path = path;
1980 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1982 /* if we got this far, there was an error; cleanup */
1989 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1990 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1992 // this function may be called very early when the templates are not set (yet), hence the checks
1994 update_title (void) {
1995 Behaviour *b = &uzbl.behave;
1998 if (b->show_status) {
1999 if (b->title_format_short) {
2000 parsed = expand(b->title_format_short, 0);
2001 if (uzbl.gui.main_window)
2002 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2005 if (b->status_format) {
2006 parsed = expand(b->status_format, 0);
2007 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2010 if (b->status_background) {
2012 gdk_color_parse (b->status_background, &color);
2013 //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)
2014 if (uzbl.gui.main_window)
2015 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2016 else if (uzbl.gui.plug)
2017 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2020 if (b->title_format_long) {
2021 parsed = expand(b->title_format_long, 0);
2022 if (uzbl.gui.main_window)
2023 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2030 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2034 retrieve_geometry();
2039 key_press_cb (GtkWidget* window, GdkEventKey* event)
2041 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2045 if (event->type != GDK_KEY_PRESS ||
2046 event->keyval == GDK_Page_Up ||
2047 event->keyval == GDK_Page_Down ||
2048 event->keyval == GDK_Up ||
2049 event->keyval == GDK_Down ||
2050 event->keyval == GDK_Left ||
2051 event->keyval == GDK_Right ||
2052 event->keyval == GDK_Shift_L ||
2053 event->keyval == GDK_Shift_R)
2056 /* turn off insert mode (if always_insert_mode is not used) */
2057 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2058 set_insert_mode(uzbl.behave.always_insert_mode);
2063 if (uzbl.behave.insert_mode &&
2064 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2065 (!uzbl.behave.modmask)
2070 if (event->keyval == GDK_Escape) {
2073 dehilight(uzbl.gui.web_view, NULL, NULL);
2077 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2078 if (event->keyval == GDK_Insert) {
2080 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2081 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2083 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2086 GString* keycmd = g_string_new(uzbl.state.keycmd);
2087 g_string_append (keycmd, str);
2088 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2095 if (event->keyval == GDK_BackSpace)
2096 keycmd_bs(NULL, NULL, NULL);
2098 gboolean key_ret = FALSE;
2099 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2102 GString* keycmd = g_string_new(uzbl.state.keycmd);
2103 g_string_append(keycmd, event->string);
2104 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2107 run_keycmd(key_ret);
2109 if (key_ret) return (!uzbl.behave.insert_mode);
2114 run_keycmd(const gboolean key_ret) {
2115 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2117 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2119 parse_command(act->name, act->param, NULL);
2123 /* try if it's an incremental keycmd or one that takes args, and run it */
2124 GString* short_keys = g_string_new ("");
2125 GString* short_keys_inc = g_string_new ("");
2127 guint len = strlen(uzbl.state.keycmd);
2128 for (i=0; i<len; i++) {
2129 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2130 g_string_assign(short_keys_inc, short_keys->str);
2131 g_string_append_c(short_keys, '_');
2132 g_string_append_c(short_keys_inc, '*');
2134 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2135 /* run normal cmds only if return was pressed */
2136 exec_paramcmd(act, i);
2139 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2140 if (key_ret) /* just quit the incremental command on return */
2142 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2146 g_string_truncate(short_keys, short_keys->len - 1);
2148 g_string_free (short_keys, TRUE);
2149 g_string_free (short_keys_inc, TRUE);
2153 exec_paramcmd(const Action *act, const guint i) {
2154 GString *parampart = g_string_new (uzbl.state.keycmd);
2155 GString *actionname = g_string_new ("");
2156 GString *actionparam = g_string_new ("");
2157 g_string_erase (parampart, 0, i+1);
2159 g_string_printf (actionname, act->name, parampart->str);
2161 g_string_printf (actionparam, act->param, parampart->str);
2162 parse_command(actionname->str, actionparam->str, NULL);
2163 g_string_free(actionname, TRUE);
2164 g_string_free(actionparam, TRUE);
2165 g_string_free(parampart, TRUE);
2173 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2174 //main_window_ref = g_object_ref(scrolled_window);
2175 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2177 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2178 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2180 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2181 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2182 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2183 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2184 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2185 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2186 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2187 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2188 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2189 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2190 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2192 return scrolled_window;
2199 g->mainbar = gtk_hbox_new (FALSE, 0);
2201 /* keep a reference to the bar so we can re-pack it at runtime*/
2202 //sbar_ref = g_object_ref(g->mainbar);
2204 g->mainbar_label = gtk_label_new ("");
2205 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2206 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2207 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2208 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2209 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2210 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2215 GtkWidget* create_window () {
2216 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2217 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2218 gtk_widget_set_name (window, "Uzbl browser");
2219 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2220 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2221 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2227 GtkPlug* create_plug () {
2228 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2229 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2230 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2237 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2239 If actname is one that calls an external command, this function will inject
2240 newargs in front of the user-provided args in that command line. They will
2241 come become after the body of the script (in sh) or after the name of
2242 the command to execute (in spawn).
2243 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2244 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2246 The return value consist of two strings: the action (sh, ...) and its args.
2248 If act is not one that calls an external command, then the given action merely
2251 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2252 gchar *actdup = g_strdup(actname);
2253 g_array_append_val(rets, actdup);
2255 if ((g_strcmp0(actname, "spawn") == 0) ||
2256 (g_strcmp0(actname, "sh") == 0) ||
2257 (g_strcmp0(actname, "sync_spawn") == 0) ||
2258 (g_strcmp0(actname, "sync_sh") == 0)) {
2260 GString *a = g_string_new("");
2261 gchar **spawnparts = split_quoted(origargs, FALSE);
2262 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2263 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2265 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2266 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2268 g_array_append_val(rets, a->str);
2269 g_string_free(a, FALSE);
2270 g_strfreev(spawnparts);
2272 gchar *origdup = g_strdup(origargs);
2273 g_array_append_val(rets, origdup);
2275 return (gchar**)g_array_free(rets, FALSE);
2279 run_handler (const gchar *act, const gchar *args) {
2280 /* Consider this code a temporary hack to make the handlers usable.
2281 In practice, all this splicing, injection, and reconstruction is
2282 inefficient, annoying and hard to manage. Potential pitfalls arise
2283 when the handler specific args 1) are not quoted (the handler
2284 callbacks should take care of this) 2) are quoted but interfere
2285 with the users' own quotation. A more ideal solution is
2286 to refactor parse_command so that it doesn't just take a string
2287 and execute it; rather than that, we should have a function which
2288 returns the argument vector parsed from the string. This vector
2289 could be modified (e.g. insert additional args into it) before
2290 passing it to the next function that actually executes it. Though
2291 it still isn't perfect for chain actions.. will reconsider & re-
2292 factor when I have the time. -duc */
2294 char **parts = g_strsplit(act, " ", 2);
2296 if (g_strcmp0(parts[0], "chain") == 0) {
2297 GString *newargs = g_string_new("");
2298 gchar **chainparts = split_quoted(parts[1], FALSE);
2300 /* for every argument in the chain, inject the handler args
2301 and make sure the new parts are wrapped in quotes */
2302 gchar **cp = chainparts;
2304 gchar *quotless = NULL;
2305 gchar **spliced_quotless = NULL; // sigh -_-;
2306 gchar **inpart = NULL;
2309 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2311 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2312 } else quotless = g_strdup(*cp);
2314 spliced_quotless = g_strsplit(quotless, " ", 2);
2315 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2316 g_strfreev(spliced_quotless);
2318 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2324 parse_command(parts[0], &(newargs->str[1]), NULL);
2325 g_string_free(newargs, TRUE);
2326 g_strfreev(chainparts);
2329 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2330 parse_command(inparts[0], inparts[1], NULL);
2338 add_binding (const gchar *key, const gchar *act) {
2339 char **parts = g_strsplit(act, " ", 2);
2346 if (uzbl.state.verbose)
2347 printf ("Binding %-10s : %s\n", key, act);
2348 action = new_action(parts[0], parts[1]);
2350 if (g_hash_table_remove (uzbl.bindings, key))
2351 g_warning ("Overwriting existing binding for \"%s\"", key);
2352 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2357 get_xdg_var (XDG_Var xdg) {
2358 const gchar* actual_value = getenv (xdg.environmental);
2359 const gchar* home = getenv ("HOME");
2360 gchar* return_value;
2362 if (! actual_value || strcmp (actual_value, "") == 0) {
2363 if (xdg.default_value) {
2364 return_value = str_replace ("~", home, xdg.default_value);
2366 return_value = NULL;
2369 return_value = str_replace("~", home, actual_value);
2372 return return_value;
2376 find_xdg_file (int xdg_type, char* filename) {
2377 /* xdg_type = 0 => config
2378 xdg_type = 1 => data
2379 xdg_type = 2 => cache*/
2381 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2382 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2385 gchar* temporary_string;
2389 if (! file_exists (temporary_file) && xdg_type != 2) {
2390 buf = get_xdg_var (XDG[3 + xdg_type]);
2391 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2394 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2395 g_free (temporary_file);
2396 temporary_file = g_strconcat (temporary_string, filename, NULL);
2400 //g_free (temporary_string); - segfaults.
2402 if (file_exists (temporary_file)) {
2403 return temporary_file;
2410 State *s = &uzbl.state;
2411 Network *n = &uzbl.net;
2413 for (i = 0; default_config[i].command != NULL; i++) {
2414 parse_cmd_line(default_config[i].command, NULL);
2417 if (g_strcmp0(s->config_file, "-") == 0) {
2418 s->config_file = NULL;
2422 else if (!s->config_file) {
2423 s->config_file = find_xdg_file (0, "/uzbl/config");
2426 if (s->config_file) {
2427 GArray* lines = read_file_by_line (s->config_file);
2431 while ((line = g_array_index(lines, gchar*, i))) {
2432 parse_cmd_line (line, NULL);
2436 g_array_free (lines, TRUE);
2438 if (uzbl.state.verbose)
2439 printf ("No configuration file loaded.\n");
2442 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2445 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2448 if (!uzbl.behave.cookie_handler)
2451 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2452 GString *s = g_string_new ("");
2453 SoupURI * soup_uri = soup_message_get_uri(msg);
2454 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2455 run_handler(uzbl.behave.cookie_handler, s->str);
2457 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2458 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2459 if ( p != NULL ) *p = '\0';
2460 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2462 if (uzbl.comm.sync_stdout)
2463 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2465 g_string_free(s, TRUE);
2469 save_cookies (SoupMessage *msg, gpointer user_data){
2473 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2474 cookie = soup_cookie_to_set_cookie_header(ck->data);
2475 SoupURI * soup_uri = soup_message_get_uri(msg);
2476 GString *s = g_string_new ("");
2477 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2478 run_handler(uzbl.behave.cookie_handler, s->str);
2480 g_string_free(s, TRUE);
2485 /* --- WEBINSPECTOR --- */
2487 hide_window_cb(GtkWidget *widget, gpointer data) {
2490 gtk_widget_hide(widget);
2493 static WebKitWebView*
2494 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2497 (void) web_inspector;
2498 GtkWidget* scrolled_window;
2499 GtkWidget* new_web_view;
2502 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2503 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2504 G_CALLBACK(hide_window_cb), NULL);
2506 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2507 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2508 gtk_widget_show(g->inspector_window);
2510 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2511 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2512 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2513 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2514 gtk_widget_show(scrolled_window);
2516 new_web_view = webkit_web_view_new();
2517 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2519 return WEBKIT_WEB_VIEW(new_web_view);
2523 inspector_show_window_cb (WebKitWebInspector* inspector){
2525 gtk_widget_show(uzbl.gui.inspector_window);
2529 /* TODO: Add variables and code to make use of these functions */
2531 inspector_close_window_cb (WebKitWebInspector* inspector){
2537 inspector_attach_window_cb (WebKitWebInspector* inspector){
2543 inspector_detach_window_cb (WebKitWebInspector* inspector){
2549 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2555 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2561 set_up_inspector() {
2563 WebKitWebSettings *settings = view_settings();
2564 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2566 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2567 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2568 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2569 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2570 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2571 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2572 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2574 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2578 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2580 uzbl_cmdprop *c = v;
2585 if(c->type == TYPE_STR)
2586 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2587 else if(c->type == TYPE_INT)
2588 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2592 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2596 printf("bind %s = %s %s\n", (char *)k ,
2597 (char *)a->name, a->param?(char *)a->param:"");
2601 dump_config() { //ADD "result" var so we can use this with uzblctrl
2602 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2603 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2607 retrieve_geometry() {
2609 GString *buf = g_string_new("");
2611 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2612 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2614 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2616 if(uzbl.gui.geometry)
2617 g_free(uzbl.gui.geometry);
2618 uzbl.gui.geometry = g_string_free(buf, FALSE);
2621 /* set up gtk, gobject, variable defaults and other things that tests and other
2622 * external applications need to do anyhow */
2624 initialize(int argc, char *argv[]) {
2625 gtk_init (&argc, &argv);
2626 if (!g_thread_supported ())
2627 g_thread_init (NULL);
2628 uzbl.state.executable_path = g_strdup(argv[0]);
2629 uzbl.state.selected_url = NULL;
2630 uzbl.state.searchtx = NULL;
2632 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2633 g_option_context_add_main_entries (context, entries, NULL);
2634 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2635 g_option_context_parse (context, &argc, &argv, NULL);
2636 g_option_context_free(context);
2638 if (uzbl.behave.print_version) {
2639 printf("Commit: %s\n", COMMIT);
2643 /* initialize hash table */
2644 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2646 uzbl.net.soup_session = webkit_get_default_session();
2647 uzbl.state.keycmd = g_strdup("");
2649 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2650 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2651 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2652 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2653 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2654 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2656 uzbl.gui.sbar.progress_s = g_strdup("=");
2657 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2658 uzbl.gui.sbar.progress_w = 10;
2660 /* HTML mode defaults*/
2661 uzbl.behave.html_buffer = g_string_new("");
2662 uzbl.behave.html_endmarker = g_strdup(".");
2663 uzbl.behave.html_timeout = 60;
2664 uzbl.behave.base_url = g_strdup("http://invalid");
2666 /* default mode indicators */
2667 uzbl.behave.insert_indicator = g_strdup("I");
2668 uzbl.behave.cmd_indicator = g_strdup("C");
2670 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2671 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2672 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2673 uzbl.info.arch = ARCH;
2674 uzbl.info.commit = COMMIT;
2677 make_var_to_name_hash();
2679 uzbl.gui.scrolled_win = create_browser();
2682 #ifndef UZBL_LIBRARY
2685 main (int argc, char* argv[]) {
2686 initialize(argc, argv);
2688 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2692 /* initial packing */
2693 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2694 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2696 if (uzbl.state.socket_id) {
2697 uzbl.gui.plug = create_plug ();
2698 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2699 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2701 uzbl.gui.main_window = create_window ();
2702 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2703 gtk_widget_show_all (uzbl.gui.main_window);
2704 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2707 if(!uzbl.state.instance_name)
2708 uzbl.state.instance_name = itos((int)uzbl.xwin);
2710 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2712 if (uzbl.state.verbose) {
2713 printf("Uzbl start location: %s\n", argv[0]);
2714 if (uzbl.state.socket_id)
2715 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2717 printf("window_id %i\n",(int) uzbl.xwin);
2718 printf("pid %i\n", getpid ());
2719 printf("name: %s\n", uzbl.state.instance_name);
2722 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2723 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2724 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2725 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2726 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2728 if(uzbl.gui.geometry)
2731 retrieve_geometry();
2733 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2734 gboolean verbose_override = uzbl.state.verbose;
2737 set_insert_mode(FALSE);
2739 if (!uzbl.behave.show_status)
2740 gtk_widget_hide(uzbl.gui.mainbar);
2747 if (verbose_override > uzbl.state.verbose)
2748 uzbl.state.verbose = verbose_override;
2751 set_var_value("uri", uri_override);
2752 g_free(uri_override);
2753 } else if (uzbl.state.uri)
2754 cmd_load_uri(uzbl.gui.web_view, NULL);
2759 return EXIT_SUCCESS;
2763 /* vi: set et ts=4: */