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 { "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 {
91 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
93 /* an abbreviation to help keep the table's width humane */
94 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* --------------------------------------------------------------------------------------- */
102 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
104 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
105 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
106 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
107 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
108 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
109 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
110 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
111 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
112 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
113 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
114 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
115 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
116 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
117 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
118 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
119 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
120 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
121 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
122 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
123 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
124 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
125 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
126 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
127 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
128 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
129 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
130 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
131 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
132 { "new_window", PTR(uzbl.behave.new_window, STR, 1, cmd_new_window)},
133 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
134 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
135 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
136 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
137 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
138 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
139 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
140 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
141 /* exported WebKitWebSettings properties */
142 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
143 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
144 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
145 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
146 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
147 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
148 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
149 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
150 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
151 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
152 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
153 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
154 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
155 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
156 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
157 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
159 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
160 }, *n2v_p = var_name_to_ptr;
166 { "SHIFT", GDK_SHIFT_MASK }, // shift
167 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
168 { "CONTROL", GDK_CONTROL_MASK }, // control
169 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
170 { "MOD2", GDK_MOD2_MASK }, // 5th mod
171 { "MOD3", GDK_MOD3_MASK }, // 6th mod
172 { "MOD4", GDK_MOD4_MASK }, // 7th mod
173 { "MOD5", GDK_MOD5_MASK }, // 8th mod
174 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
175 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
176 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
177 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
178 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
179 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
180 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
181 { "META", GDK_META_MASK }, // meta (since 2.10)
186 /* construct a hash from the var_name_to_ptr array for quick access */
188 make_var_to_name_hash() {
189 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
191 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
196 /* --- UTILITY FUNCTIONS --- */
198 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
200 get_exp_type(gchar *s) {
204 else if(*(s+1) == '{')
205 return EXP_BRACED_VAR;
206 else if(*(s+1) == '<')
209 return EXP_SIMPLE_VAR;
215 * recurse == 1: don't expand '@(command)@'
216 * recurse == 2: don't expand '@<java script>@'
219 expand(char *s, guint recurse) {
223 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
228 gchar *cmd_stdout = NULL;
230 GString *buf = g_string_new("");
231 GString *js_ret = g_string_new("");
236 g_string_append_c(buf, *++s);
241 etype = get_exp_type(s);
246 if( (vend = strpbrk(s, end_simple_var)) ||
247 (vend = strchr(s, '\0')) ) {
248 strncpy(ret, s, vend-s);
254 if( (vend = strchr(s, upto)) ||
255 (vend = strchr(s, '\0')) ) {
256 strncpy(ret, s, vend-s);
262 strcpy(str_end, ")@");
264 if( (vend = strstr(s, str_end)) ||
265 (vend = strchr(s, '\0')) ) {
266 strncpy(ret, s, vend-s);
272 strcpy(str_end, ">@");
274 if( (vend = strstr(s, str_end)) ||
275 (vend = strchr(s, '\0')) ) {
276 strncpy(ret, s, vend-s);
282 if(etype == EXP_SIMPLE_VAR ||
283 etype == EXP_BRACED_VAR) {
284 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
285 if(c->type == TYPE_STR)
286 g_string_append(buf, (gchar *)*c->ptr);
287 else if(c->type == TYPE_INT) {
288 char *b = itos((int)*c->ptr);
289 g_string_append(buf, b);
293 if(etype == EXP_SIMPLE_VAR)
298 else if(recurse != 1 &&
300 mycmd = expand(ret, 1);
301 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
305 g_printerr("error on running command: %s\n", err->message);
308 else if (*cmd_stdout) {
309 g_string_append(buf, cmd_stdout);
314 else if(recurse != 2 &&
316 mycmd = expand(ret, 2);
317 eval_js(uzbl.gui.web_view, mycmd, js_ret);
321 g_string_append(buf, js_ret->str);
322 g_string_free(js_ret, TRUE);
323 js_ret = g_string_new("");
330 g_string_append_c(buf, *s);
335 g_string_free(js_ret, TRUE);
336 return g_string_free(buf, FALSE);
343 snprintf(tmp, sizeof(tmp), "%i", val);
344 return g_strdup(tmp);
348 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
351 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
354 str_replace (const char* search, const char* replace, const char* string) {
358 buf = g_strsplit (string, search, -1);
359 ret = g_strjoinv (replace, buf);
360 g_strfreev(buf); // somebody said this segfaults
366 read_file_by_line (gchar *path) {
367 GIOChannel *chan = NULL;
368 gchar *readbuf = NULL;
370 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
373 chan = g_io_channel_new_file(path, "r", NULL);
376 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
377 const gchar* val = g_strdup (readbuf);
378 g_array_append_val (lines, val);
383 g_io_channel_unref (chan);
385 fprintf(stderr, "File '%s' not be read.\n", path);
392 gchar* parseenv (char* string) {
393 extern char** environ;
394 gchar* tmpstr = NULL;
398 while (environ[i] != NULL) {
399 gchar** env = g_strsplit (environ[i], "=", 2);
400 gchar* envname = g_strconcat ("$", env[0], NULL);
402 if (g_strrstr (string, envname) != NULL) {
403 tmpstr = g_strdup(string);
405 string = str_replace(envname, env[1], tmpstr);
410 g_strfreev (env); // somebody said this breaks uzbl
418 setup_signal(int signr, sigfunc *shandler) {
419 struct sigaction nh, oh;
421 nh.sa_handler = shandler;
422 sigemptyset(&nh.sa_mask);
425 if(sigaction(signr, &nh, &oh) < 0)
433 if (uzbl.behave.fifo_dir)
434 unlink (uzbl.comm.fifo_path);
435 if (uzbl.behave.socket_dir)
436 unlink (uzbl.comm.socket_path);
438 g_free(uzbl.state.executable_path);
439 g_string_free(uzbl.state.keycmd, TRUE);
440 g_hash_table_destroy(uzbl.bindings);
441 g_hash_table_destroy(uzbl.behave.commands);
444 /* used for html_mode_timeout
445 * be sure to extend this function to use
446 * more timers if needed in other places
449 set_timeout(int seconds) {
451 memset(&t, 0, sizeof t);
453 t.it_value.tv_sec = seconds;
454 t.it_value.tv_usec = 0;
455 setitimer(ITIMER_REAL, &t, NULL);
458 /* --- SIGNAL HANDLER --- */
461 catch_sigterm(int s) {
467 catch_sigint(int s) {
477 set_var_value("mode", "0");
482 /* --- CALLBACKS --- */
485 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
488 (void) navigation_action;
489 (void) policy_decision;
491 const gchar* uri = webkit_network_request_get_uri (request);
492 if (uzbl.state.verbose)
493 printf("New window requested -> %s \n", uri);
494 webkit_web_policy_decision_use(policy_decision);
499 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
504 /* If we can display it, let's display it... */
505 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
506 webkit_web_policy_decision_use (policy_decision);
510 /* ...everything we can't displayed is downloaded */
511 webkit_web_policy_decision_download (policy_decision);
516 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
520 if (uzbl.state.selected_url != NULL) {
521 if (uzbl.state.verbose)
522 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
523 new_window_load_uri(uzbl.state.selected_url);
525 if (uzbl.state.verbose)
526 printf("New web view -> %s\n","Nothing to open, exiting");
532 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
535 if (uzbl.behave.download_handler) {
536 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
537 if (uzbl.state.verbose)
538 printf("Download -> %s\n",uri);
539 /* if urls not escaped, we may have to escape and quote uri before this call */
540 run_handler(uzbl.behave.download_handler, uri);
545 /* scroll a bar in a given direction */
547 scroll (GtkAdjustment* bar, GArray *argv) {
551 gdouble page_size = gtk_adjustment_get_page_size(bar);
552 gdouble value = gtk_adjustment_get_value(bar);
553 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
556 value += page_size * amount * 0.01;
560 max_value = gtk_adjustment_get_upper(bar) - page_size;
562 if (value > max_value)
563 value = max_value; /* don't scroll past the end of the page */
565 gtk_adjustment_set_value (bar, value);
569 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
570 (void) page; (void) argv; (void) result;
571 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
575 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
576 (void) page; (void) argv; (void) result;
577 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
578 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
582 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
583 (void) page; (void) result;
584 scroll(uzbl.gui.bar_v, argv);
588 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
589 (void) page; (void) result;
590 scroll(uzbl.gui.bar_h, argv);
595 if (!uzbl.behave.show_status) {
596 gtk_widget_hide(uzbl.gui.mainbar);
598 gtk_widget_show(uzbl.gui.mainbar);
604 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
609 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
613 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
618 if (uzbl.behave.show_status) {
619 gtk_widget_hide(uzbl.gui.mainbar);
621 gtk_widget_show(uzbl.gui.mainbar);
623 uzbl.behave.show_status = !uzbl.behave.show_status;
628 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
632 //Set selected_url state variable
633 g_free(uzbl.state.selected_url);
634 uzbl.state.selected_url = NULL;
636 uzbl.state.selected_url = g_strdup(link);
642 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
645 const gchar *title = webkit_web_view_get_title(web_view);
646 if (uzbl.gui.main_title)
647 g_free (uzbl.gui.main_title);
648 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
653 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
656 uzbl.gui.sbar.load_progress = progress;
661 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
665 if (uzbl.behave.load_finish_handler)
666 run_handler(uzbl.behave.load_finish_handler, "");
670 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
674 uzbl.gui.sbar.load_progress = 0;
675 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
676 if (uzbl.behave.load_start_handler)
677 run_handler(uzbl.behave.load_start_handler, "");
681 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
684 g_free (uzbl.state.uri);
685 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
686 uzbl.state.uri = g_string_free (newuri, FALSE);
687 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
688 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
691 if (uzbl.behave.load_commit_handler)
692 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
696 destroy_cb (GtkWidget* widget, gpointer data) {
704 if (uzbl.behave.history_handler) {
706 struct tm * timeinfo;
709 timeinfo = localtime ( &rawtime );
710 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
711 run_handler(uzbl.behave.history_handler, date);
716 /* VIEW funcs (little webkit wrappers) */
717 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
719 VIEWFUNC(reload_bypass_cache)
720 VIEWFUNC(stop_loading)
727 /* -- command to callback/function map for things we cannot attach to any signals */
728 static struct {char *key; CommandInfo value;} cmdlist[] =
729 { /* key function no_split */
730 { "back", {view_go_back, 0} },
731 { "forward", {view_go_forward, 0} },
732 { "scroll_vert", {scroll_vert, 0} },
733 { "scroll_horz", {scroll_horz, 0} },
734 { "scroll_begin", {scroll_begin, 0} },
735 { "scroll_end", {scroll_end, 0} },
736 { "reload", {view_reload, 0}, },
737 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
738 { "stop", {view_stop_loading, 0}, },
739 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
740 { "zoom_out", {view_zoom_out, 0}, },
741 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
742 { "uri", {load_uri, TRUE} },
743 { "js", {run_js, TRUE} },
744 { "script", {run_external_js, 0} },
745 { "toggle_status", {toggle_status_cb, 0} },
746 { "spawn", {spawn, 0} },
747 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
748 { "sh", {spawn_sh, 0} },
749 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
750 { "exit", {close_uzbl, 0} },
751 { "search", {search_forward_text, TRUE} },
752 { "search_reverse", {search_reverse_text, TRUE} },
753 { "dehilight", {dehilight, 0} },
754 { "toggle_insert_mode", {toggle_insert_mode, 0} },
755 { "set", {set_var, TRUE} },
756 //{ "get", {get_var, TRUE} },
757 { "bind", {act_bind, TRUE} },
758 { "dump_config", {act_dump_config, 0} },
759 { "keycmd", {keycmd, TRUE} },
760 { "keycmd_nl", {keycmd_nl, TRUE} },
761 { "keycmd_bs", {keycmd_bs, 0} },
762 { "chain", {chain, 0} },
763 { "print", {print, TRUE} }
770 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
772 for (i = 0; i < LENGTH(cmdlist); i++)
773 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
776 /* -- CORE FUNCTIONS -- */
779 free_action(gpointer act) {
780 Action *action = (Action*)act;
781 g_free(action->name);
783 g_free(action->param);
788 new_action(const gchar *name, const gchar *param) {
789 Action *action = g_new(Action, 1);
791 action->name = g_strdup(name);
793 action->param = g_strdup(param);
795 action->param = NULL;
801 file_exists (const char * filename) {
802 return (access(filename, F_OK) == 0);
806 set_var(WebKitWebView *page, GArray *argv, GString *result) {
807 (void) page; (void) result;
808 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
809 if (split[0] != NULL) {
810 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
811 set_var_value(g_strstrip(split[0]), value);
818 print(WebKitWebView *page, GArray *argv, GString *result) {
819 (void) page; (void) result;
822 buf = expand(argv_idx(argv, 0), 0);
823 g_string_assign(result, buf);
828 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
829 (void) page; (void) result;
830 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
831 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
832 add_binding(g_strstrip(split[0]), value);
844 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
845 (void) page; (void) result;
847 if (argv_idx(argv, 0)) {
848 if (strcmp (argv_idx(argv, 0), "0") == 0) {
849 uzbl.behave.insert_mode = FALSE;
851 uzbl.behave.insert_mode = TRUE;
854 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
861 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
864 if (argv_idx(argv, 0)) {
865 GString* newuri = g_string_new (argv_idx(argv, 0));
866 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
867 run_js(web_view, argv, NULL);
870 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
871 g_string_prepend (newuri, "http://");
872 /* if we do handle cookies, ask our handler for them */
873 webkit_web_view_load_uri (web_view, newuri->str);
874 g_string_free (newuri, TRUE);
882 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
883 size_t argumentCount, const JSValueRef arguments[],
884 JSValueRef* exception) {
889 JSStringRef js_result_string;
890 GString *result = g_string_new("");
892 if (argumentCount >= 1) {
893 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
894 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
895 char ctl_line[arg_size];
896 JSStringGetUTF8CString(arg, ctl_line, arg_size);
898 parse_cmd_line(ctl_line, result);
900 JSStringRelease(arg);
902 js_result_string = JSStringCreateWithUTF8CString(result->str);
904 g_string_free(result, TRUE);
906 return JSValueMakeString(ctx, js_result_string);
909 static JSStaticFunction js_static_functions[] = {
910 {"run", js_run_command, kJSPropertyAttributeNone},
915 /* This function creates the class and its definition, only once */
916 if (!uzbl.js.initialized) {
917 /* it would be pretty cool to make this dynamic */
918 uzbl.js.classdef = kJSClassDefinitionEmpty;
919 uzbl.js.classdef.staticFunctions = js_static_functions;
921 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
927 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
928 WebKitWebFrame *frame;
929 JSGlobalContextRef context;
930 JSObjectRef globalobject;
931 JSStringRef var_name;
933 JSStringRef js_script;
934 JSValueRef js_result;
935 JSStringRef js_result_string;
936 size_t js_result_size;
940 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
941 context = webkit_web_frame_get_global_context(frame);
942 globalobject = JSContextGetGlobalObject(context);
944 /* uzbl javascript namespace */
945 var_name = JSStringCreateWithUTF8CString("Uzbl");
946 JSObjectSetProperty(context, globalobject, var_name,
947 JSObjectMake(context, uzbl.js.classref, NULL),
948 kJSClassAttributeNone, NULL);
950 /* evaluate the script and get return value*/
951 js_script = JSStringCreateWithUTF8CString(script);
952 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
953 if (js_result && !JSValueIsUndefined(context, js_result)) {
954 js_result_string = JSValueToStringCopy(context, js_result, NULL);
955 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
957 if (js_result_size) {
958 char js_result_utf8[js_result_size];
959 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
960 g_string_assign(result, js_result_utf8);
963 JSStringRelease(js_result_string);
967 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
969 JSStringRelease(var_name);
970 JSStringRelease(js_script);
974 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
976 if (argv_idx(argv, 0))
977 eval_js(web_view, argv_idx(argv, 0), result);
981 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
983 if (argv_idx(argv, 0)) {
984 GArray* lines = read_file_by_line (argv_idx (argv, 0));
989 while ((line = g_array_index(lines, gchar*, i))) {
991 js = g_strdup (line);
993 gchar* newjs = g_strconcat (js, line, NULL);
1000 if (uzbl.state.verbose)
1001 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1003 if (argv_idx (argv, 1)) {
1004 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1008 eval_js (web_view, js, result);
1010 g_array_free (lines, TRUE);
1015 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1016 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1017 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1018 webkit_web_view_unmark_text_matches (page);
1019 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1020 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1024 if (uzbl.state.searchtx) {
1025 if (uzbl.state.verbose)
1026 printf ("Searching: %s\n", uzbl.state.searchtx);
1027 webkit_web_view_set_highlight_text_matches (page, TRUE);
1028 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1033 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1035 search_text(page, argv, TRUE);
1039 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1041 search_text(page, argv, FALSE);
1045 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1046 (void) argv; (void) result;
1047 webkit_web_view_set_highlight_text_matches (page, FALSE);
1052 new_window_load_uri (const gchar * uri) {
1053 if (uzbl.behave.new_window) {
1054 GString *s = g_string_new ("");
1055 g_string_printf(s, "'%s'", uri);
1056 run_handler(uzbl.behave.new_window, s->str);
1059 GString* to_execute = g_string_new ("");
1060 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1062 for (i = 0; entries[i].long_name != NULL; i++) {
1063 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1064 gchar** str = (gchar**)entries[i].arg_data;
1066 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1070 if (uzbl.state.verbose)
1071 printf("\n%s\n", to_execute->str);
1072 g_spawn_command_line_async (to_execute->str, NULL);
1073 g_string_free (to_execute, TRUE);
1077 chain (WebKitWebView *page, GArray *argv, GString *result) {
1078 (void) page; (void) result;
1080 gchar **parts = NULL;
1082 while ((a = argv_idx(argv, i++))) {
1083 parts = g_strsplit (a, " ", 2);
1084 parse_command(parts[0], parts[1], result);
1090 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1094 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1100 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1104 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1110 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1115 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1117 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1122 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1129 /* --Statusbar functions-- */
1131 build_progressbar_ascii(int percent) {
1132 int width=uzbl.gui.sbar.progress_w;
1135 GString *bar = g_string_new("");
1137 l = (double)percent*((double)width/100.);
1138 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1140 for(i=0; i<(int)l; i++)
1141 g_string_append(bar, uzbl.gui.sbar.progress_s);
1144 g_string_append(bar, uzbl.gui.sbar.progress_u);
1146 return g_string_free(bar, FALSE);
1151 const GScannerConfig scan_config = {
1154 ) /* cset_skip_characters */,
1159 ) /* cset_identifier_first */,
1166 ) /* cset_identifier_nth */,
1167 ( "" ) /* cpair_comment_single */,
1169 TRUE /* case_sensitive */,
1171 FALSE /* skip_comment_multi */,
1172 FALSE /* skip_comment_single */,
1173 FALSE /* scan_comment_multi */,
1174 TRUE /* scan_identifier */,
1175 TRUE /* scan_identifier_1char */,
1176 FALSE /* scan_identifier_NULL */,
1177 TRUE /* scan_symbols */,
1178 FALSE /* scan_binary */,
1179 FALSE /* scan_octal */,
1180 FALSE /* scan_float */,
1181 FALSE /* scan_hex */,
1182 FALSE /* scan_hex_dollar */,
1183 FALSE /* scan_string_sq */,
1184 FALSE /* scan_string_dq */,
1185 TRUE /* numbers_2_int */,
1186 FALSE /* int_2_float */,
1187 FALSE /* identifier_2_string */,
1188 FALSE /* char_2_token */,
1189 FALSE /* symbol_2_token */,
1190 TRUE /* scope_0_fallback */,
1195 uzbl.scan = g_scanner_new(&scan_config);
1196 while(symp->symbol_name) {
1197 g_scanner_scope_add_symbol(uzbl.scan, 0,
1199 GINT_TO_POINTER(symp->symbol_token));
1205 expand_template(const char *template, gboolean escape_markup) {
1206 if(!template) return NULL;
1208 GTokenType token = G_TOKEN_NONE;
1209 GString *ret = g_string_new("");
1213 g_scanner_input_text(uzbl.scan, template, strlen(template));
1214 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1215 token = g_scanner_get_next_token(uzbl.scan);
1217 if(token == G_TOKEN_SYMBOL) {
1218 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1222 buf = uzbl.state.uri?
1223 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1224 g_string_append(ret, buf);
1228 g_string_append(ret, uzbl.state.uri?
1229 uzbl.state.uri:g_strdup(""));
1232 buf = itos(uzbl.gui.sbar.load_progress);
1233 g_string_append(ret, buf);
1236 case SYM_LOADPRGSBAR:
1237 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1238 g_string_append(ret, buf);
1243 buf = uzbl.gui.main_title?
1244 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1245 g_string_append(ret, buf);
1249 g_string_append(ret, uzbl.gui.main_title?
1250 uzbl.gui.main_title:g_strdup(""));
1252 case SYM_SELECTED_URI:
1254 buf = uzbl.state.selected_url?
1255 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1256 g_string_append(ret, buf);
1260 g_string_append(ret, uzbl.state.selected_url?
1261 uzbl.state.selected_url:g_strdup(""));
1264 buf = itos(uzbl.xwin);
1265 g_string_append(ret,
1266 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1271 buf = uzbl.state.keycmd->str?
1272 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1273 g_string_append(ret, buf);
1277 g_string_append(ret, uzbl.state.keycmd->str?
1278 uzbl.state.keycmd->str:g_strdup(""));
1281 g_string_append(ret,
1282 uzbl.behave.insert_mode?
1283 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1286 g_string_append(ret,
1287 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1289 /* useragent syms */
1291 buf = itos(WEBKIT_MAJOR_VERSION);
1292 g_string_append(ret, buf);
1296 buf = itos(WEBKIT_MINOR_VERSION);
1297 g_string_append(ret, buf);
1301 buf = itos(WEBKIT_MICRO_VERSION);
1302 g_string_append(ret, buf);
1306 g_string_append(ret, uzbl.state.unameinfo.sysname);
1309 g_string_append(ret, uzbl.state.unameinfo.nodename);
1312 g_string_append(ret, uzbl.state.unameinfo.release);
1315 g_string_append(ret, uzbl.state.unameinfo.version);
1318 g_string_append(ret, uzbl.state.unameinfo.machine);
1321 g_string_append(ret, ARCH);
1324 case SYM_DOMAINNAME:
1325 g_string_append(ret, uzbl.state.unameinfo.domainname);
1329 g_string_append(ret, COMMIT);
1335 else if(token == G_TOKEN_INT) {
1336 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1337 g_string_append(ret, buf);
1340 else if(token == G_TOKEN_IDENTIFIER) {
1341 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1343 else if(token == G_TOKEN_CHAR) {
1344 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1348 return g_string_free(ret, FALSE);
1350 /* --End Statusbar functions-- */
1353 sharg_append(GArray *a, const gchar *str) {
1354 const gchar *s = (str ? str : "");
1355 g_array_append_val(a, s);
1358 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1360 run_command (const gchar *command, const guint npre, const gchar **args,
1361 const gboolean sync, char **output_stdout) {
1362 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1365 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1366 gchar *pid = itos(getpid());
1367 gchar *xwin = itos(uzbl.xwin);
1369 sharg_append(a, command);
1370 for (i = 0; i < npre; i++) /* add n args before the default vars */
1371 sharg_append(a, args[i]);
1372 sharg_append(a, uzbl.state.config_file);
1373 sharg_append(a, pid);
1374 sharg_append(a, xwin);
1375 sharg_append(a, uzbl.comm.fifo_path);
1376 sharg_append(a, uzbl.comm.socket_path);
1377 sharg_append(a, uzbl.state.uri);
1378 sharg_append(a, uzbl.gui.main_title);
1380 for (i = npre; i < g_strv_length((gchar**)args); i++)
1381 sharg_append(a, args[i]);
1385 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1387 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1388 NULL, NULL, output_stdout, NULL, NULL, &err);
1389 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1390 NULL, NULL, NULL, &err);
1392 if (uzbl.state.verbose) {
1393 GString *s = g_string_new("spawned:");
1394 for (i = 0; i < (a->len); i++) {
1395 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1396 g_string_append_printf(s, " %s", qarg);
1399 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1400 printf("%s\n", s->str);
1401 g_string_free(s, TRUE);
1403 printf("Stdout: %s\n", *output_stdout);
1407 g_printerr("error on run_command: %s\n", err->message);
1412 g_array_free (a, TRUE);
1417 split_quoted(const gchar* src, const gboolean unquote) {
1418 /* split on unquoted space, return array of strings;
1419 remove a layer of quotes and backslashes if unquote */
1420 if (!src) return NULL;
1422 gboolean dq = FALSE;
1423 gboolean sq = FALSE;
1424 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1425 GString *s = g_string_new ("");
1429 for (p = src; *p != '\0'; p++) {
1430 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1431 else if (*p == '\\') { g_string_append_c(s, *p++);
1432 g_string_append_c(s, *p); }
1433 else if ((*p == '"') && unquote && !sq) dq = !dq;
1434 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1436 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1437 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1439 else if ((*p == ' ') && !dq && !sq) {
1440 dup = g_strdup(s->str);
1441 g_array_append_val(a, dup);
1442 g_string_truncate(s, 0);
1443 } else g_string_append_c(s, *p);
1445 dup = g_strdup(s->str);
1446 g_array_append_val(a, dup);
1447 ret = (gchar**)a->data;
1448 g_array_free (a, FALSE);
1449 g_string_free (s, TRUE);
1454 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1455 (void)web_view; (void)result;
1456 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1457 if (argv_idx(argv, 0))
1458 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1462 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1463 (void)web_view; (void)result;
1465 if (argv_idx(argv, 0))
1466 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1467 TRUE, &uzbl.comm.sync_stdout);
1471 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1472 (void)web_view; (void)result;
1473 if (!uzbl.behave.shell_cmd) {
1474 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1479 gchar *spacer = g_strdup("");
1480 g_array_insert_val(argv, 1, spacer);
1481 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1483 for (i = 1; i < g_strv_length(cmd); i++)
1484 g_array_prepend_val(argv, cmd[i]);
1486 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1492 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1493 (void)web_view; (void)result;
1494 if (!uzbl.behave.shell_cmd) {
1495 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1500 gchar *spacer = g_strdup("");
1501 g_array_insert_val(argv, 1, spacer);
1502 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1504 for (i = 1; i < g_strv_length(cmd); i++)
1505 g_array_prepend_val(argv, cmd[i]);
1507 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1508 TRUE, &uzbl.comm.sync_stdout);
1514 parse_command(const char *cmd, const char *param, GString *result) {
1517 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1519 gchar **par = split_quoted(param, TRUE);
1520 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1522 if (c->no_split) { /* don't split */
1523 sharg_append(a, param);
1525 for (i = 0; i < g_strv_length(par); i++)
1526 sharg_append(a, par[i]);
1529 if (result == NULL) {
1530 GString *result_print = g_string_new("");
1532 c->function(uzbl.gui.web_view, a, result_print);
1533 if (result_print->len)
1534 printf("%*s\n", result_print->len, result_print->str);
1536 g_string_free(result_print, TRUE);
1538 c->function(uzbl.gui.web_view, a, result);
1541 g_array_free (a, TRUE);
1544 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1551 if(*uzbl.net.proxy_url == ' '
1552 || uzbl.net.proxy_url == NULL) {
1553 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1554 (GType) SOUP_SESSION_PROXY_URI);
1557 suri = soup_uri_new(uzbl.net.proxy_url);
1558 g_object_set(G_OBJECT(uzbl.net.soup_session),
1559 SOUP_SESSION_PROXY_URI,
1561 soup_uri_free(suri);
1568 if(file_exists(uzbl.gui.icon)) {
1569 if (uzbl.gui.main_window)
1570 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1572 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1578 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1579 g_array_append_val (a, uzbl.state.uri);
1580 load_uri(uzbl.gui.web_view, a, NULL);
1581 g_array_free (a, TRUE);
1585 cmd_always_insert_mode() {
1586 uzbl.behave.insert_mode =
1587 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1593 g_object_set(G_OBJECT(uzbl.net.soup_session),
1594 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1598 cmd_max_conns_host() {
1599 g_object_set(G_OBJECT(uzbl.net.soup_session),
1600 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1605 soup_session_remove_feature
1606 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1607 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1608 /*g_free(uzbl.net.soup_logger);*/
1610 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1611 soup_session_add_feature(uzbl.net.soup_session,
1612 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1615 static WebKitWebSettings*
1617 return webkit_web_view_get_settings(uzbl.gui.web_view);
1622 WebKitWebSettings *ws = view_settings();
1623 if (uzbl.behave.font_size > 0) {
1624 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1627 if (uzbl.behave.monospace_size > 0) {
1628 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1629 uzbl.behave.monospace_size, NULL);
1631 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1632 uzbl.behave.font_size, NULL);
1638 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1642 cmd_disable_plugins() {
1643 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1644 !uzbl.behave.disable_plugins, NULL);
1648 cmd_disable_scripts() {
1649 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1650 !uzbl.behave.disable_scripts, NULL);
1654 cmd_minimum_font_size() {
1655 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1656 uzbl.behave.minimum_font_size, NULL);
1659 cmd_autoload_img() {
1660 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1661 uzbl.behave.autoload_img, NULL);
1666 cmd_autoshrink_img() {
1667 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1668 uzbl.behave.autoshrink_img, NULL);
1673 cmd_enable_spellcheck() {
1674 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1675 uzbl.behave.enable_spellcheck, NULL);
1679 cmd_enable_private() {
1680 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1681 uzbl.behave.enable_private, NULL);
1686 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1687 uzbl.behave.print_bg, NULL);
1692 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1693 uzbl.behave.style_uri, NULL);
1697 cmd_resizable_txt() {
1698 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1699 uzbl.behave.resizable_txt, NULL);
1703 cmd_default_encoding() {
1704 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1705 uzbl.behave.default_encoding, NULL);
1709 cmd_enforce_96dpi() {
1710 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1711 uzbl.behave.enforce_96dpi, NULL);
1715 cmd_caret_browsing() {
1716 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1717 uzbl.behave.caret_browsing, NULL);
1721 cmd_cookie_handler() {
1722 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1723 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1724 if ((g_strcmp0(split[0], "sh") == 0) ||
1725 (g_strcmp0(split[0], "spawn") == 0)) {
1726 g_free (uzbl.behave.cookie_handler);
1727 uzbl.behave.cookie_handler =
1728 g_strdup_printf("sync_%s %s", split[0], split[1]);
1735 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1736 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1737 if ((g_strcmp0(split[0], "sh") == 0) ||
1738 (g_strcmp0(split[0], "spawn") == 0)) {
1739 g_free (uzbl.behave.new_window);
1740 uzbl.behave.new_window =
1741 g_strdup_printf("%s %s", split[0], split[1]);
1748 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1753 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1758 if(uzbl.behave.inject_html) {
1759 webkit_web_view_load_html_string (uzbl.gui.web_view,
1760 uzbl.behave.inject_html, NULL);
1769 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1770 uzbl.behave.modmask = 0;
1772 if(uzbl.behave.modkey)
1773 g_free(uzbl.behave.modkey);
1774 uzbl.behave.modkey = buf;
1776 for (i = 0; modkeys[i].key != NULL; i++) {
1777 if (g_strrstr(buf, modkeys[i].key))
1778 uzbl.behave.modmask |= modkeys[i].mask;
1784 if (*uzbl.net.useragent == ' ') {
1785 g_free (uzbl.net.useragent);
1786 uzbl.net.useragent = NULL;
1788 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1790 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1791 g_free(uzbl.net.useragent);
1792 uzbl.net.useragent = ua;
1798 gtk_widget_ref(uzbl.gui.scrolled_win);
1799 gtk_widget_ref(uzbl.gui.mainbar);
1800 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1801 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1803 if(uzbl.behave.status_top) {
1804 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1805 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1808 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1809 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1811 gtk_widget_unref(uzbl.gui.scrolled_win);
1812 gtk_widget_unref(uzbl.gui.mainbar);
1813 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1818 set_var_value(gchar *name, gchar *val) {
1819 uzbl_cmdprop *c = NULL;
1823 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1824 /* check for the variable type */
1825 if (c->type == TYPE_STR) {
1826 buf = expand(val, 0);
1829 } else if(c->type == TYPE_INT) {
1830 int *ip = (int *)c->ptr;
1831 buf = expand(val, 0);
1832 *ip = (int)strtoul(buf, &endp, 10);
1834 } else if (c->type == TYPE_FLOAT) {
1835 float *fp = (float *)c->ptr;
1836 buf = expand(val, 0);
1837 *fp = strtod(buf, &endp);
1841 /* invoke a command specific function */
1842 if(c->func) c->func();
1849 Behaviour *b = &uzbl.behave;
1851 if(b->html_buffer->str) {
1852 webkit_web_view_load_html_string (uzbl.gui.web_view,
1853 b->html_buffer->str, b->base_url);
1854 g_string_free(b->html_buffer, TRUE);
1855 b->html_buffer = g_string_new("");
1859 enum {M_CMD, M_HTML};
1861 parse_cmd_line(const char *ctl_line, GString *result) {
1862 Behaviour *b = &uzbl.behave;
1865 if(b->mode == M_HTML) {
1866 len = strlen(b->html_endmarker);
1867 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1868 if(len == strlen(ctl_line)-1 &&
1869 !strncmp(b->html_endmarker, ctl_line, len)) {
1871 set_var_value("mode", "0");
1876 set_timeout(b->html_timeout);
1877 g_string_append(b->html_buffer, ctl_line);
1880 else if((ctl_line[0] == '#') /* Comments */
1881 || (ctl_line[0] == ' ')
1882 || (ctl_line[0] == '\n'))
1883 ; /* ignore these lines */
1884 else { /* parse a command */
1886 gchar **tokens = NULL;
1887 len = strlen(ctl_line);
1889 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1890 ctlstrip = g_strndup(ctl_line, len - 1);
1891 else ctlstrip = g_strdup(ctl_line);
1893 tokens = g_strsplit(ctlstrip, " ", 2);
1894 parse_command(tokens[0], tokens[1], result);
1901 build_stream_name(int type, const gchar* dir) {
1902 char *xwin_str = NULL;
1903 State *s = &uzbl.state;
1906 xwin_str = itos((int)uzbl.xwin);
1908 str = g_strdup_printf
1909 ("%s/uzbl_fifo_%s", dir,
1910 s->instance_name ? s->instance_name : xwin_str);
1911 } else if (type == SOCKET) {
1912 str = g_strdup_printf
1913 ("%s/uzbl_socket_%s", dir,
1914 s->instance_name ? s->instance_name : xwin_str );
1921 control_fifo(GIOChannel *gio, GIOCondition condition) {
1922 if (uzbl.state.verbose)
1923 printf("triggered\n");
1928 if (condition & G_IO_HUP)
1929 g_error ("Fifo: Read end of pipe died!\n");
1932 g_error ("Fifo: GIOChannel broke\n");
1934 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1935 if (ret == G_IO_STATUS_ERROR) {
1936 g_error ("Fifo: Error reading: %s\n", err->message);
1940 parse_cmd_line(ctl_line, NULL);
1947 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1948 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1949 if (unlink(uzbl.comm.fifo_path) == -1)
1950 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1951 g_free(uzbl.comm.fifo_path);
1952 uzbl.comm.fifo_path = NULL;
1955 if (*dir == ' ') { /* space unsets the variable */
1960 GIOChannel *chan = NULL;
1961 GError *error = NULL;
1962 gchar *path = build_stream_name(FIFO, dir);
1964 if (!file_exists(path)) {
1965 if (mkfifo (path, 0666) == 0) {
1966 // 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.
1967 chan = g_io_channel_new_file(path, "r+", &error);
1969 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1970 if (uzbl.state.verbose)
1971 printf ("init_fifo: created successfully as %s\n", path);
1972 uzbl.comm.fifo_path = path;
1974 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1975 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1976 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1977 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1979 /* if we got this far, there was an error; cleanup */
1980 if (error) g_error_free (error);
1987 control_stdin(GIOChannel *gio, GIOCondition condition) {
1989 gchar *ctl_line = NULL;
1992 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1993 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1996 parse_cmd_line(ctl_line, NULL);
2004 GIOChannel *chan = NULL;
2005 GError *error = NULL;
2007 chan = g_io_channel_unix_new(fileno(stdin));
2009 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2010 g_error ("Stdin: could not add watch\n");
2012 if (uzbl.state.verbose)
2013 printf ("Stdin: watch added successfully\n");
2016 g_error ("Stdin: Error while opening: %s\n", error->message);
2018 if (error) g_error_free (error);
2022 control_socket(GIOChannel *chan) {
2023 struct sockaddr_un remote;
2024 unsigned int t = sizeof(remote);
2026 GIOChannel *clientchan;
2028 clientsock = accept (g_io_channel_unix_get_fd(chan),
2029 (struct sockaddr *) &remote, &t);
2031 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2032 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2033 (GIOFunc) control_client_socket, clientchan);
2040 control_client_socket(GIOChannel *clientchan) {
2042 GString *result = g_string_new("");
2043 GError *error = NULL;
2047 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2048 if (ret == G_IO_STATUS_ERROR) {
2049 g_warning ("Error reading: %s\n", error->message);
2050 g_io_channel_shutdown(clientchan, TRUE, &error);
2052 } else if (ret == G_IO_STATUS_EOF) {
2053 /* shutdown and remove channel watch from main loop */
2054 g_io_channel_shutdown(clientchan, TRUE, &error);
2059 parse_cmd_line (ctl_line, result);
2060 g_string_append_c(result, '\n');
2061 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2063 if (ret == G_IO_STATUS_ERROR) {
2064 g_warning ("Error writing: %s", error->message);
2066 g_io_channel_flush(clientchan, &error);
2069 if (error) g_error_free (error);
2070 g_string_free(result, TRUE);
2076 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2077 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2078 if (unlink(uzbl.comm.socket_path) == -1)
2079 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2080 g_free(uzbl.comm.socket_path);
2081 uzbl.comm.socket_path = NULL;
2089 GIOChannel *chan = NULL;
2091 struct sockaddr_un local;
2092 gchar *path = build_stream_name(SOCKET, dir);
2094 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2096 local.sun_family = AF_UNIX;
2097 strcpy (local.sun_path, path);
2098 unlink (local.sun_path);
2100 len = strlen (local.sun_path) + sizeof (local.sun_family);
2101 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2102 if (uzbl.state.verbose)
2103 printf ("init_socket: opened in %s\n", path);
2106 if( (chan = g_io_channel_unix_new(sock)) ) {
2107 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2108 uzbl.comm.socket_path = path;
2111 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2113 /* if we got this far, there was an error; cleanup */
2120 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2121 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2123 // this function may be called very early when the templates are not set (yet), hence the checks
2125 update_title (void) {
2126 Behaviour *b = &uzbl.behave;
2129 if (b->show_status) {
2130 if (b->title_format_short) {
2131 parsed = expand_template(b->title_format_short, FALSE);
2132 if (uzbl.gui.main_window)
2133 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2136 if (b->status_format) {
2137 parsed = expand_template(b->status_format, TRUE);
2138 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2141 if (b->status_background) {
2143 gdk_color_parse (b->status_background, &color);
2144 //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)
2145 if (uzbl.gui.main_window)
2146 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2147 else if (uzbl.gui.plug)
2148 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2151 if (b->title_format_long) {
2152 parsed = expand_template(b->title_format_long, FALSE);
2153 if (uzbl.gui.main_window)
2154 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2161 key_press_cb (GtkWidget* window, GdkEventKey* event)
2163 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2167 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2168 || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
2171 /* turn off insert mode (if always_insert_mode is not used) */
2172 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2173 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2178 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2181 if (event->keyval == GDK_Escape) {
2182 g_string_truncate(uzbl.state.keycmd, 0);
2184 dehilight(uzbl.gui.web_view, NULL, NULL);
2188 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2189 if (event->keyval == GDK_Insert) {
2191 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2192 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2194 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2197 g_string_append (uzbl.state.keycmd, str);
2204 if (event->keyval == GDK_BackSpace)
2205 keycmd_bs(NULL, NULL, NULL);
2207 gboolean key_ret = FALSE;
2208 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2210 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2212 run_keycmd(key_ret);
2214 if (key_ret) return (!uzbl.behave.insert_mode);
2219 run_keycmd(const gboolean key_ret) {
2220 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2222 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2223 g_string_truncate(uzbl.state.keycmd, 0);
2224 parse_command(act->name, act->param, NULL);
2228 /* try if it's an incremental keycmd or one that takes args, and run it */
2229 GString* short_keys = g_string_new ("");
2230 GString* short_keys_inc = g_string_new ("");
2232 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2233 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2234 g_string_assign(short_keys_inc, short_keys->str);
2235 g_string_append_c(short_keys, '_');
2236 g_string_append_c(short_keys_inc, '*');
2238 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2239 /* run normal cmds only if return was pressed */
2240 exec_paramcmd(act, i);
2241 g_string_truncate(uzbl.state.keycmd, 0);
2243 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2244 if (key_ret) /* just quit the incremental command on return */
2245 g_string_truncate(uzbl.state.keycmd, 0);
2246 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2250 g_string_truncate(short_keys, short_keys->len - 1);
2252 g_string_free (short_keys, TRUE);
2253 g_string_free (short_keys_inc, TRUE);
2257 exec_paramcmd(const Action *act, const guint i) {
2258 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2259 GString *actionname = g_string_new ("");
2260 GString *actionparam = g_string_new ("");
2261 g_string_erase (parampart, 0, i+1);
2263 g_string_printf (actionname, act->name, parampart->str);
2265 g_string_printf (actionparam, act->param, parampart->str);
2266 parse_command(actionname->str, actionparam->str, NULL);
2267 g_string_free(actionname, TRUE);
2268 g_string_free(actionparam, TRUE);
2269 g_string_free(parampart, TRUE);
2277 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2278 //main_window_ref = g_object_ref(scrolled_window);
2279 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
2281 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2282 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2284 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2285 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2286 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2287 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2288 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2289 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2290 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2291 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2292 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2293 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2294 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2296 return scrolled_window;
2303 g->mainbar = gtk_hbox_new (FALSE, 0);
2305 /* keep a reference to the bar so we can re-pack it at runtime*/
2306 //sbar_ref = g_object_ref(g->mainbar);
2308 g->mainbar_label = gtk_label_new ("");
2309 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2310 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2311 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2312 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2313 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2314 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2319 GtkWidget* create_window () {
2320 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2321 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2322 gtk_widget_set_name (window, "Uzbl browser");
2323 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2324 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2330 GtkPlug* create_plug () {
2331 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2332 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2333 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2340 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2342 If actname is one that calls an external command, this function will inject
2343 newargs in front of the user-provided args in that command line. They will
2344 come become after the body of the script (in sh) or after the name of
2345 the command to execute (in spawn).
2346 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2347 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2349 The return value consist of two strings: the action (sh, ...) and its args.
2351 If act is not one that calls an external command, then the given action merely
2354 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2355 gchar *actdup = g_strdup(actname);
2356 g_array_append_val(rets, actdup);
2358 if ((g_strcmp0(actname, "spawn") == 0) ||
2359 (g_strcmp0(actname, "sh") == 0) ||
2360 (g_strcmp0(actname, "sync_spawn") == 0) ||
2361 (g_strcmp0(actname, "sync_sh") == 0)) {
2363 GString *a = g_string_new("");
2364 gchar **spawnparts = split_quoted(origargs, FALSE);
2365 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2366 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2368 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2369 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2371 g_array_append_val(rets, a->str);
2372 g_string_free(a, FALSE);
2373 g_strfreev(spawnparts);
2375 gchar *origdup = g_strdup(origargs);
2376 g_array_append_val(rets, origdup);
2378 return (gchar**)g_array_free(rets, FALSE);
2382 run_handler (const gchar *act, const gchar *args) {
2383 /* Consider this code a temporary hack to make the handlers usable.
2384 In practice, all this splicing, injection, and reconstruction is
2385 inefficient, annoying and hard to manage. Potential pitfalls arise
2386 when the handler specific args 1) are not quoted (the handler
2387 callbacks should take care of this) 2) are quoted but interfere
2388 with the users' own quotation. A more ideal solution is
2389 to refactor parse_command so that it doesn't just take a string
2390 and execute it; rather than that, we should have a function which
2391 returns the argument vector parsed from the string. This vector
2392 could be modified (e.g. insert additional args into it) before
2393 passing it to the next function that actually executes it. Though
2394 it still isn't perfect for chain actions.. will reconsider & re-
2395 factor when I have the time. -duc */
2397 char **parts = g_strsplit(act, " ", 2);
2399 if (g_strcmp0(parts[0], "chain") == 0) {
2400 GString *newargs = g_string_new("");
2401 gchar **chainparts = split_quoted(parts[1], FALSE);
2403 /* for every argument in the chain, inject the handler args
2404 and make sure the new parts are wrapped in quotes */
2405 gchar **cp = chainparts;
2407 gchar *quotless = NULL;
2408 gchar **spliced_quotless = NULL; // sigh -_-;
2409 gchar **inpart = NULL;
2412 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2414 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2415 } else quotless = g_strdup(*cp);
2417 spliced_quotless = g_strsplit(quotless, " ", 2);
2418 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2419 g_strfreev(spliced_quotless);
2421 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2427 parse_command(parts[0], &(newargs->str[1]), NULL);
2428 g_string_free(newargs, TRUE);
2429 g_strfreev(chainparts);
2432 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2433 parse_command(inparts[0], inparts[1], NULL);
2441 add_binding (const gchar *key, const gchar *act) {
2442 char **parts = g_strsplit(act, " ", 2);
2449 if (uzbl.state.verbose)
2450 printf ("Binding %-10s : %s\n", key, act);
2451 action = new_action(parts[0], parts[1]);
2453 if (g_hash_table_remove (uzbl.bindings, key))
2454 g_warning ("Overwriting existing binding for \"%s\"", key);
2455 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2460 get_xdg_var (XDG_Var xdg) {
2461 const gchar* actual_value = getenv (xdg.environmental);
2462 const gchar* home = getenv ("HOME");
2463 gchar* return_value;
2465 if (! actual_value || strcmp (actual_value, "") == 0) {
2466 if (xdg.default_value) {
2467 return_value = str_replace ("~", home, xdg.default_value);
2469 return_value = NULL;
2472 return_value = str_replace("~", home, actual_value);
2475 return return_value;
2479 find_xdg_file (int xdg_type, char* filename) {
2480 /* xdg_type = 0 => config
2481 xdg_type = 1 => data
2482 xdg_type = 2 => cache*/
2484 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2485 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2488 gchar* temporary_string;
2492 if (! file_exists (temporary_file) && xdg_type != 2) {
2493 buf = get_xdg_var (XDG[3 + xdg_type]);
2494 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2497 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2498 g_free (temporary_file);
2499 temporary_file = g_strconcat (temporary_string, filename, NULL);
2503 //g_free (temporary_string); - segfaults.
2505 if (file_exists (temporary_file)) {
2506 return temporary_file;
2513 State *s = &uzbl.state;
2514 Network *n = &uzbl.net;
2516 for (i = 0; default_config[i].command != NULL; i++) {
2517 parse_cmd_line(default_config[i].command, NULL);
2520 if (g_strcmp0(s->config_file, "-") == 0) {
2521 s->config_file = NULL;
2525 else if (!s->config_file) {
2526 s->config_file = find_xdg_file (0, "/uzbl/config");
2529 if (s->config_file) {
2530 GArray* lines = read_file_by_line (s->config_file);
2534 while ((line = g_array_index(lines, gchar*, i))) {
2535 parse_cmd_line (line, NULL);
2539 g_array_free (lines, TRUE);
2541 if (uzbl.state.verbose)
2542 printf ("No configuration file loaded.\n");
2545 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2548 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2551 if (!uzbl.behave.cookie_handler)
2554 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2555 GString *s = g_string_new ("");
2556 SoupURI * soup_uri = soup_message_get_uri(msg);
2557 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2558 run_handler(uzbl.behave.cookie_handler, s->str);
2560 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2561 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2562 if ( p != NULL ) *p = '\0';
2563 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2565 if (uzbl.comm.sync_stdout)
2566 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2568 g_string_free(s, TRUE);
2572 save_cookies (SoupMessage *msg, gpointer user_data){
2576 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2577 cookie = soup_cookie_to_set_cookie_header(ck->data);
2578 SoupURI * soup_uri = soup_message_get_uri(msg);
2579 GString *s = g_string_new ("");
2580 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2581 run_handler(uzbl.behave.cookie_handler, s->str);
2583 g_string_free(s, TRUE);
2588 /* --- WEBINSPECTOR --- */
2590 hide_window_cb(GtkWidget *widget, gpointer data) {
2593 gtk_widget_hide(widget);
2596 static WebKitWebView*
2597 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2600 (void) web_inspector;
2601 GtkWidget* scrolled_window;
2602 GtkWidget* new_web_view;
2605 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2606 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2607 G_CALLBACK(hide_window_cb), NULL);
2609 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2610 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2611 gtk_widget_show(g->inspector_window);
2613 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2614 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2615 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2616 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2617 gtk_widget_show(scrolled_window);
2619 new_web_view = webkit_web_view_new();
2620 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2622 return WEBKIT_WEB_VIEW(new_web_view);
2626 inspector_show_window_cb (WebKitWebInspector* inspector){
2628 gtk_widget_show(uzbl.gui.inspector_window);
2632 /* TODO: Add variables and code to make use of these functions */
2634 inspector_close_window_cb (WebKitWebInspector* inspector){
2640 inspector_attach_window_cb (WebKitWebInspector* inspector){
2646 inspector_detach_window_cb (WebKitWebInspector* inspector){
2652 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2658 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2664 set_up_inspector() {
2666 WebKitWebSettings *settings = view_settings();
2667 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2669 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2670 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2671 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2672 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2673 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2674 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2675 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2677 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2681 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2683 uzbl_cmdprop *c = v;
2688 if(c->type == TYPE_STR)
2689 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2690 else if(c->type == TYPE_INT)
2691 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2695 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2699 printf("bind %s = %s %s\n", (char *)k ,
2700 (char *)a->name, a->param?(char *)a->param:"");
2705 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2706 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2711 main (int argc, char* argv[]) {
2712 gtk_init (&argc, &argv);
2713 if (!g_thread_supported ())
2714 g_thread_init (NULL);
2715 uzbl.state.executable_path = g_strdup(argv[0]);
2716 uzbl.state.selected_url = NULL;
2717 uzbl.state.searchtx = NULL;
2719 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2720 g_option_context_add_main_entries (context, entries, NULL);
2721 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2722 g_option_context_parse (context, &argc, &argv, NULL);
2723 g_option_context_free(context);
2725 if (uzbl.behave.print_version) {
2726 printf("Commit: %s\n", COMMIT);
2730 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2731 if (argc > 1 && !uzbl.state.uri)
2732 uri_override = g_strdup(argv[1]);
2733 gboolean verbose_override = uzbl.state.verbose;
2735 /* initialize hash table */
2736 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2738 uzbl.net.soup_session = webkit_get_default_session();
2739 uzbl.state.keycmd = g_string_new("");
2741 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2742 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2743 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2744 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2745 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2746 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2749 if(uname(&uzbl.state.unameinfo) == -1)
2750 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2752 uzbl.gui.sbar.progress_s = g_strdup("=");
2753 uzbl.gui.sbar.progress_u = g_strdup("·");
2754 uzbl.gui.sbar.progress_w = 10;
2756 /* HTML mode defaults*/
2757 uzbl.behave.html_buffer = g_string_new("");
2758 uzbl.behave.html_endmarker = g_strdup(".");
2759 uzbl.behave.html_timeout = 60;
2760 uzbl.behave.base_url = g_strdup("http://invalid");
2762 /* default mode indicators */
2763 uzbl.behave.insert_indicator = g_strdup("I");
2764 uzbl.behave.cmd_indicator = g_strdup("C");
2768 make_var_to_name_hash();
2770 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2772 uzbl.gui.scrolled_win = create_browser();
2775 /* initial packing */
2776 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2777 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2779 if (uzbl.state.socket_id) {
2780 uzbl.gui.plug = create_plug ();
2781 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2782 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2784 uzbl.gui.main_window = create_window ();
2785 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2786 gtk_widget_show_all (uzbl.gui.main_window);
2787 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2790 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2792 if (uzbl.state.verbose) {
2793 printf("Uzbl start location: %s\n", argv[0]);
2794 if (uzbl.state.socket_id)
2795 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2797 printf("window_id %i\n",(int) uzbl.xwin);
2798 printf("pid %i\n", getpid ());
2799 printf("name: %s\n", uzbl.state.instance_name);
2802 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2803 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2804 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2805 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2806 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2810 if (!uzbl.behave.show_status)
2811 gtk_widget_hide(uzbl.gui.mainbar);
2818 if (verbose_override > uzbl.state.verbose)
2819 uzbl.state.verbose = verbose_override;
2822 set_var_value("uri", uri_override);
2823 g_free(uri_override);
2824 } else if (uzbl.state.uri)
2825 cmd_load_uri(uzbl.gui.web_view, NULL);
2830 return EXIT_SUCCESS;
2833 /* vi: set et ts=4: */