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 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
163 { "SHIFT", GDK_SHIFT_MASK }, // shift
164 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
165 { "CONTROL", GDK_CONTROL_MASK }, // control
166 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
167 { "MOD2", GDK_MOD2_MASK }, // 5th mod
168 { "MOD3", GDK_MOD3_MASK }, // 6th mod
169 { "MOD4", GDK_MOD4_MASK }, // 7th mod
170 { "MOD5", GDK_MOD5_MASK }, // 8th mod
171 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
177 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
178 { "META", GDK_META_MASK }, // meta (since 2.10)
183 /* construct a hash from the var_name_to_ptr array for quick access */
185 make_var_to_name_hash() {
186 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
188 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
193 /* --- UTILITY FUNCTIONS --- */
195 expand_vars(char *s) {
198 char ret[256], *vend;
199 GString *buf = g_string_new("");
204 g_string_append_c(buf, *++s);
212 if( (vend = strchr(s, upto)) ||
213 (vend = strchr(s, '\0')) ) {
214 strncpy(ret, s, vend-s);
216 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
217 if(c->type == TYPE_STR)
218 g_string_append(buf, (gchar *)*c->ptr);
219 else if(c->type == TYPE_INT) {
220 char *b = itos((int)*c->ptr);
221 g_string_append(buf, b);
225 if(upto == ' ') s = vend;
231 g_string_append_c(buf, *s);
236 return g_string_free(buf, FALSE);
243 snprintf(tmp, sizeof(tmp), "%i", val);
244 return g_strdup(tmp);
248 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
251 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
254 str_replace (const char* search, const char* replace, const char* string) {
258 buf = g_strsplit (string, search, -1);
259 ret = g_strjoinv (replace, buf);
260 g_strfreev(buf); // somebody said this segfaults
266 read_file_by_line (gchar *path) {
267 GIOChannel *chan = NULL;
268 gchar *readbuf = NULL;
270 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
273 chan = g_io_channel_new_file(path, "r", NULL);
276 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
277 const gchar* val = g_strdup (readbuf);
278 g_array_append_val (lines, val);
283 g_io_channel_unref (chan);
285 fprintf(stderr, "File '%s' not be read.\n", path);
292 gchar* parseenv (char* string) {
293 extern char** environ;
294 gchar* tmpstr = NULL;
298 while (environ[i] != NULL) {
299 gchar** env = g_strsplit (environ[i], "=", 2);
300 gchar* envname = g_strconcat ("$", env[0], NULL);
302 if (g_strrstr (string, envname) != NULL) {
303 tmpstr = g_strdup(string);
305 string = str_replace(envname, env[1], tmpstr);
310 g_strfreev (env); // somebody said this breaks uzbl
318 setup_signal(int signr, sigfunc *shandler) {
319 struct sigaction nh, oh;
321 nh.sa_handler = shandler;
322 sigemptyset(&nh.sa_mask);
325 if(sigaction(signr, &nh, &oh) < 0)
333 if (uzbl.behave.fifo_dir)
334 unlink (uzbl.comm.fifo_path);
335 if (uzbl.behave.socket_dir)
336 unlink (uzbl.comm.socket_path);
338 g_free(uzbl.state.executable_path);
339 g_string_free(uzbl.state.keycmd, TRUE);
340 g_hash_table_destroy(uzbl.bindings);
341 g_hash_table_destroy(uzbl.behave.commands);
344 /* used for html_mode_timeout
345 * be sure to extend this function to use
346 * more timers if needed in other places
349 set_timeout(int seconds) {
351 memset(&t, 0, sizeof t);
353 t.it_value.tv_sec = seconds;
354 t.it_value.tv_usec = 0;
355 setitimer(ITIMER_REAL, &t, NULL);
358 /* --- SIGNAL HANDLER --- */
361 catch_sigterm(int s) {
367 catch_sigint(int s) {
377 set_var_value("mode", "0");
382 /* --- CALLBACKS --- */
385 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
388 (void) navigation_action;
389 (void) policy_decision;
391 const gchar* uri = webkit_network_request_get_uri (request);
392 if (uzbl.state.verbose)
393 printf("New window requested -> %s \n", uri);
394 new_window_load_uri(uri);
399 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
404 /* If we can display it, let's display it... */
405 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
406 webkit_web_policy_decision_use (policy_decision);
410 /* ...everything we can't displayed is downloaded */
411 webkit_web_policy_decision_download (policy_decision);
416 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
420 if (uzbl.state.selected_url != NULL) {
421 if (uzbl.state.verbose)
422 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
423 new_window_load_uri(uzbl.state.selected_url);
425 if (uzbl.state.verbose)
426 printf("New web view -> %s\n","Nothing to open, exiting");
432 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
435 if (uzbl.behave.download_handler) {
436 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
437 if (uzbl.state.verbose)
438 printf("Download -> %s\n",uri);
439 /* if urls not escaped, we may have to escape and quote uri before this call */
440 run_handler(uzbl.behave.download_handler, uri);
445 /* scroll a bar in a given direction */
447 scroll (GtkAdjustment* bar, GArray *argv) {
451 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
452 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
453 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
457 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
458 (void) page; (void) argv; (void) result;
459 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
463 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
464 (void) page; (void) argv; (void) result;
465 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
466 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
470 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
471 (void) page; (void) result;
472 scroll(uzbl.gui.bar_v, argv);
476 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
477 (void) page; (void) result;
478 scroll(uzbl.gui.bar_h, argv);
483 if (!uzbl.behave.show_status) {
484 gtk_widget_hide(uzbl.gui.mainbar);
486 gtk_widget_show(uzbl.gui.mainbar);
492 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
497 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
501 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
506 if (uzbl.behave.show_status) {
507 gtk_widget_hide(uzbl.gui.mainbar);
509 gtk_widget_show(uzbl.gui.mainbar);
511 uzbl.behave.show_status = !uzbl.behave.show_status;
516 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
520 //Set selected_url state variable
521 g_free(uzbl.state.selected_url);
522 uzbl.state.selected_url = NULL;
524 uzbl.state.selected_url = g_strdup(link);
530 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
534 if (uzbl.gui.main_title)
535 g_free (uzbl.gui.main_title);
536 uzbl.gui.main_title = g_strdup (title);
541 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
544 uzbl.gui.sbar.load_progress = progress;
549 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
553 if (uzbl.behave.load_finish_handler)
554 run_handler(uzbl.behave.load_finish_handler, "");
558 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
562 uzbl.gui.sbar.load_progress = 0;
563 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
564 if (uzbl.behave.load_start_handler)
565 run_handler(uzbl.behave.load_start_handler, "");
569 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
572 g_free (uzbl.state.uri);
573 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
574 uzbl.state.uri = g_string_free (newuri, FALSE);
575 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
576 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
579 if (uzbl.behave.load_commit_handler)
580 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
584 destroy_cb (GtkWidget* widget, gpointer data) {
592 if (uzbl.behave.history_handler) {
594 struct tm * timeinfo;
597 timeinfo = localtime ( &rawtime );
598 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
599 run_handler(uzbl.behave.history_handler, date);
604 /* VIEW funcs (little webkit wrappers) */
605 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
607 VIEWFUNC(reload_bypass_cache)
608 VIEWFUNC(stop_loading)
615 /* -- command to callback/function map for things we cannot attach to any signals */
616 static struct {char *key; CommandInfo value;} cmdlist[] =
617 { /* key function no_split */
618 { "back", {view_go_back, 0} },
619 { "forward", {view_go_forward, 0} },
620 { "scroll_vert", {scroll_vert, 0} },
621 { "scroll_horz", {scroll_horz, 0} },
622 { "scroll_begin", {scroll_begin, 0} },
623 { "scroll_end", {scroll_end, 0} },
624 { "reload", {view_reload, 0}, },
625 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
626 { "stop", {view_stop_loading, 0}, },
627 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
628 { "zoom_out", {view_zoom_out, 0}, },
629 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
630 { "uri", {load_uri, TRUE} },
631 { "js", {run_js, TRUE} },
632 { "script", {run_external_js, 0} },
633 { "toggle_status", {toggle_status_cb, 0} },
634 { "spawn", {spawn, 0} },
635 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
636 { "sh", {spawn_sh, 0} },
637 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
638 { "exit", {close_uzbl, 0} },
639 { "search", {search_forward_text, TRUE} },
640 { "search_reverse", {search_reverse_text, TRUE} },
641 { "dehilight", {dehilight, 0} },
642 { "toggle_insert_mode", {toggle_insert_mode, 0} },
643 { "set", {set_var, TRUE} },
644 //{ "get", {get_var, TRUE} },
645 { "bind", {act_bind, TRUE} },
646 { "dump_config", {act_dump_config, 0} },
647 { "keycmd", {keycmd, TRUE} },
648 { "keycmd_nl", {keycmd_nl, TRUE} },
649 { "keycmd_bs", {keycmd_bs, 0} },
650 { "chain", {chain, 0} },
651 { "print", {print, TRUE} }
658 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
660 for (i = 0; i < LENGTH(cmdlist); i++)
661 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
664 /* -- CORE FUNCTIONS -- */
667 free_action(gpointer act) {
668 Action *action = (Action*)act;
669 g_free(action->name);
671 g_free(action->param);
676 new_action(const gchar *name, const gchar *param) {
677 Action *action = g_new(Action, 1);
679 action->name = g_strdup(name);
681 action->param = g_strdup(param);
683 action->param = NULL;
689 file_exists (const char * filename) {
690 return (access(filename, F_OK) == 0);
694 set_var(WebKitWebView *page, GArray *argv, GString *result) {
695 (void) page; (void) result;
696 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
697 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
698 set_var_value(g_strstrip(split[0]), value);
704 print(WebKitWebView *page, GArray *argv, GString *result) {
705 (void) page; (void) result;
708 buf = expand_vars(argv_idx(argv, 0));
709 g_string_assign(result, buf);
714 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
715 (void) page; (void) result;
716 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
717 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
718 add_binding(g_strstrip(split[0]), value);
730 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
731 (void) page; (void) result;
733 if (argv_idx(argv, 0)) {
734 if (strcmp (argv_idx(argv, 0), "0") == 0) {
735 uzbl.behave.insert_mode = FALSE;
737 uzbl.behave.insert_mode = TRUE;
740 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
747 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
750 if (argv_idx(argv, 0)) {
751 GString* newuri = g_string_new (argv_idx(argv, 0));
752 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
753 run_js(web_view, argv, NULL);
756 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
757 g_string_prepend (newuri, "http://");
758 /* if we do handle cookies, ask our handler for them */
759 webkit_web_view_load_uri (web_view, newuri->str);
760 g_string_free (newuri, TRUE);
768 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
769 size_t argumentCount, const JSValueRef arguments[],
770 JSValueRef* exception) {
775 JSStringRef js_result_string;
776 GString *result = g_string_new("");
778 if (argumentCount >= 1) {
779 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
780 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
781 char ctl_line[arg_size];
782 JSStringGetUTF8CString(arg, ctl_line, arg_size);
784 parse_cmd_line(ctl_line, result);
786 JSStringRelease(arg);
788 js_result_string = JSStringCreateWithUTF8CString(result->str);
790 g_string_free(result, TRUE);
792 return JSValueMakeString(ctx, js_result_string);
795 static JSStaticFunction js_static_functions[] = {
796 {"run", js_run_command, kJSPropertyAttributeNone},
801 /* This function creates the class and its definition, only once */
802 if (!uzbl.js.initialized) {
803 /* it would be pretty cool to make this dynamic */
804 uzbl.js.classdef = kJSClassDefinitionEmpty;
805 uzbl.js.classdef.staticFunctions = js_static_functions;
807 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
813 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
814 WebKitWebFrame *frame;
815 JSGlobalContextRef context;
816 JSObjectRef globalobject;
817 JSStringRef var_name;
819 JSStringRef js_script;
820 JSValueRef js_result;
821 JSStringRef js_result_string;
822 size_t js_result_size;
826 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
827 context = webkit_web_frame_get_global_context(frame);
828 globalobject = JSContextGetGlobalObject(context);
830 /* uzbl javascript namespace */
831 var_name = JSStringCreateWithUTF8CString("Uzbl");
832 JSObjectSetProperty(context, globalobject, var_name,
833 JSObjectMake(context, uzbl.js.classref, NULL),
834 kJSClassAttributeNone, NULL);
836 /* evaluate the script and get return value*/
837 js_script = JSStringCreateWithUTF8CString(script);
838 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
839 if (js_result && !JSValueIsUndefined(context, js_result)) {
840 js_result_string = JSValueToStringCopy(context, js_result, NULL);
841 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
843 if (js_result_size) {
844 char js_result_utf8[js_result_size];
845 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
846 g_string_assign(result, js_result_utf8);
849 JSStringRelease(js_result_string);
853 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
855 JSStringRelease(var_name);
856 JSStringRelease(js_script);
860 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
862 if (argv_idx(argv, 0))
863 eval_js(web_view, argv_idx(argv, 0), result);
867 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
869 if (argv_idx(argv, 0)) {
870 GArray* lines = read_file_by_line (argv_idx (argv, 0));
875 while ((line = g_array_index(lines, gchar*, i))) {
877 js = g_strdup (line);
879 gchar* newjs = g_strconcat (js, line, NULL);
886 if (uzbl.state.verbose)
887 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
889 if (argv_idx (argv, 1)) {
890 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
894 eval_js (web_view, js, result);
896 g_array_free (lines, TRUE);
901 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
902 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
903 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
904 webkit_web_view_unmark_text_matches (page);
905 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
906 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
910 if (uzbl.state.searchtx) {
911 if (uzbl.state.verbose)
912 printf ("Searching: %s\n", uzbl.state.searchtx);
913 webkit_web_view_set_highlight_text_matches (page, TRUE);
914 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
919 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
921 search_text(page, argv, TRUE);
925 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
927 search_text(page, argv, FALSE);
931 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
932 (void) argv; (void) result;
933 webkit_web_view_set_highlight_text_matches (page, FALSE);
938 new_window_load_uri (const gchar * uri) {
939 GString* to_execute = g_string_new ("");
940 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
942 for (i = 0; entries[i].long_name != NULL; i++) {
943 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
944 gchar** str = (gchar**)entries[i].arg_data;
946 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
950 if (uzbl.state.verbose)
951 printf("\n%s\n", to_execute->str);
952 g_spawn_command_line_async (to_execute->str, NULL);
953 g_string_free (to_execute, TRUE);
957 chain (WebKitWebView *page, GArray *argv, GString *result) {
958 (void) page; (void) result;
960 gchar **parts = NULL;
962 while ((a = argv_idx(argv, i++))) {
963 parts = g_strsplit (a, " ", 2);
964 parse_command(parts[0], parts[1], result);
970 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
974 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
980 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
984 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
990 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
994 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
999 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1006 /* --Statusbar functions-- */
1008 build_progressbar_ascii(int percent) {
1009 int width=uzbl.gui.sbar.progress_w;
1012 GString *bar = g_string_new("");
1014 l = (double)percent*((double)width/100.);
1015 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1017 for(i=0; i<(int)l; i++)
1018 g_string_append(bar, uzbl.gui.sbar.progress_s);
1021 g_string_append(bar, uzbl.gui.sbar.progress_u);
1023 return g_string_free(bar, FALSE);
1028 const GScannerConfig scan_config = {
1031 ) /* cset_skip_characters */,
1036 ) /* cset_identifier_first */,
1043 ) /* cset_identifier_nth */,
1044 ( "" ) /* cpair_comment_single */,
1046 TRUE /* case_sensitive */,
1048 FALSE /* skip_comment_multi */,
1049 FALSE /* skip_comment_single */,
1050 FALSE /* scan_comment_multi */,
1051 TRUE /* scan_identifier */,
1052 TRUE /* scan_identifier_1char */,
1053 FALSE /* scan_identifier_NULL */,
1054 TRUE /* scan_symbols */,
1055 FALSE /* scan_binary */,
1056 FALSE /* scan_octal */,
1057 FALSE /* scan_float */,
1058 FALSE /* scan_hex */,
1059 FALSE /* scan_hex_dollar */,
1060 FALSE /* scan_string_sq */,
1061 FALSE /* scan_string_dq */,
1062 TRUE /* numbers_2_int */,
1063 FALSE /* int_2_float */,
1064 FALSE /* identifier_2_string */,
1065 FALSE /* char_2_token */,
1066 FALSE /* symbol_2_token */,
1067 TRUE /* scope_0_fallback */,
1072 uzbl.scan = g_scanner_new(&scan_config);
1073 while(symp->symbol_name) {
1074 g_scanner_scope_add_symbol(uzbl.scan, 0,
1076 GINT_TO_POINTER(symp->symbol_token));
1082 expand_template(const char *template, gboolean escape_markup) {
1083 if(!template) return NULL;
1085 GTokenType token = G_TOKEN_NONE;
1086 GString *ret = g_string_new("");
1090 g_scanner_input_text(uzbl.scan, template, strlen(template));
1091 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1092 token = g_scanner_get_next_token(uzbl.scan);
1094 if(token == G_TOKEN_SYMBOL) {
1095 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1099 buf = uzbl.state.uri?
1100 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1101 g_string_append(ret, buf);
1105 g_string_append(ret, uzbl.state.uri?
1106 uzbl.state.uri:g_strdup(""));
1109 buf = itos(uzbl.gui.sbar.load_progress);
1110 g_string_append(ret, buf);
1113 case SYM_LOADPRGSBAR:
1114 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1115 g_string_append(ret, buf);
1120 buf = uzbl.gui.main_title?
1121 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1122 g_string_append(ret, buf);
1126 g_string_append(ret, uzbl.gui.main_title?
1127 uzbl.gui.main_title:g_strdup(""));
1129 case SYM_SELECTED_URI:
1131 buf = uzbl.state.selected_url?
1132 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1133 g_string_append(ret, buf);
1137 g_string_append(ret, uzbl.state.selected_url?
1138 uzbl.state.selected_url:g_strdup(""));
1141 buf = itos(uzbl.xwin);
1142 g_string_append(ret,
1143 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1148 buf = uzbl.state.keycmd->str?
1149 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1150 g_string_append(ret, buf);
1154 g_string_append(ret, uzbl.state.keycmd->str?
1155 uzbl.state.keycmd->str:g_strdup(""));
1158 g_string_append(ret,
1159 uzbl.behave.insert_mode?
1160 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1163 g_string_append(ret,
1164 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1166 /* useragent syms */
1168 buf = itos(WEBKIT_MAJOR_VERSION);
1169 g_string_append(ret, buf);
1173 buf = itos(WEBKIT_MINOR_VERSION);
1174 g_string_append(ret, buf);
1178 buf = itos(WEBKIT_MICRO_VERSION);
1179 g_string_append(ret, buf);
1183 g_string_append(ret, uzbl.state.unameinfo.sysname);
1186 g_string_append(ret, uzbl.state.unameinfo.nodename);
1189 g_string_append(ret, uzbl.state.unameinfo.release);
1192 g_string_append(ret, uzbl.state.unameinfo.version);
1195 g_string_append(ret, uzbl.state.unameinfo.machine);
1198 g_string_append(ret, ARCH);
1201 case SYM_DOMAINNAME:
1202 g_string_append(ret, uzbl.state.unameinfo.domainname);
1206 g_string_append(ret, COMMIT);
1212 else if(token == G_TOKEN_INT) {
1213 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1214 g_string_append(ret, buf);
1217 else if(token == G_TOKEN_IDENTIFIER) {
1218 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1220 else if(token == G_TOKEN_CHAR) {
1221 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1225 return g_string_free(ret, FALSE);
1227 /* --End Statusbar functions-- */
1230 sharg_append(GArray *a, const gchar *str) {
1231 const gchar *s = (str ? str : "");
1232 g_array_append_val(a, s);
1235 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1237 run_command (const gchar *command, const guint npre, const gchar **args,
1238 const gboolean sync, char **output_stdout) {
1239 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1242 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1243 gchar *pid = itos(getpid());
1244 gchar *xwin = itos(uzbl.xwin);
1246 sharg_append(a, command);
1247 for (i = 0; i < npre; i++) /* add n args before the default vars */
1248 sharg_append(a, args[i]);
1249 sharg_append(a, uzbl.state.config_file);
1250 sharg_append(a, pid);
1251 sharg_append(a, xwin);
1252 sharg_append(a, uzbl.comm.fifo_path);
1253 sharg_append(a, uzbl.comm.socket_path);
1254 sharg_append(a, uzbl.state.uri);
1255 sharg_append(a, uzbl.gui.main_title);
1257 for (i = npre; i < g_strv_length((gchar**)args); i++)
1258 sharg_append(a, args[i]);
1262 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1264 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1265 NULL, NULL, output_stdout, NULL, NULL, &err);
1266 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1267 NULL, NULL, NULL, &err);
1269 if (uzbl.state.verbose) {
1270 GString *s = g_string_new("spawned:");
1271 for (i = 0; i < (a->len); i++) {
1272 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1273 g_string_append_printf(s, " %s", qarg);
1276 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1277 printf("%s\n", s->str);
1278 g_string_free(s, TRUE);
1280 printf("Stdout: %s\n", *output_stdout);
1284 g_printerr("error on run_command: %s\n", err->message);
1289 g_array_free (a, TRUE);
1294 split_quoted(const gchar* src, const gboolean unquote) {
1295 /* split on unquoted space, return array of strings;
1296 remove a layer of quotes and backslashes if unquote */
1297 if (!src) return NULL;
1299 gboolean dq = FALSE;
1300 gboolean sq = FALSE;
1301 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1302 GString *s = g_string_new ("");
1306 for (p = src; *p != '\0'; p++) {
1307 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1308 else if (*p == '\\') { g_string_append_c(s, *p++);
1309 g_string_append_c(s, *p); }
1310 else if ((*p == '"') && unquote && !sq) dq = !dq;
1311 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1313 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1314 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1316 else if ((*p == ' ') && !dq && !sq) {
1317 dup = g_strdup(s->str);
1318 g_array_append_val(a, dup);
1319 g_string_truncate(s, 0);
1320 } else g_string_append_c(s, *p);
1322 dup = g_strdup(s->str);
1323 g_array_append_val(a, dup);
1324 ret = (gchar**)a->data;
1325 g_array_free (a, FALSE);
1326 g_string_free (s, TRUE);
1331 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1332 (void)web_view; (void)result;
1333 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1334 if (argv_idx(argv, 0))
1335 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1339 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1340 (void)web_view; (void)result;
1342 if (argv_idx(argv, 0))
1343 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1344 TRUE, &uzbl.comm.sync_stdout);
1348 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1349 (void)web_view; (void)result;
1350 if (!uzbl.behave.shell_cmd) {
1351 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1356 gchar *spacer = g_strdup("");
1357 g_array_insert_val(argv, 1, spacer);
1358 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1360 for (i = 1; i < g_strv_length(cmd); i++)
1361 g_array_prepend_val(argv, cmd[i]);
1363 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1369 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1370 (void)web_view; (void)result;
1371 if (!uzbl.behave.shell_cmd) {
1372 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1377 gchar *spacer = g_strdup("");
1378 g_array_insert_val(argv, 1, spacer);
1379 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1381 for (i = 1; i < g_strv_length(cmd); i++)
1382 g_array_prepend_val(argv, cmd[i]);
1384 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1385 TRUE, &uzbl.comm.sync_stdout);
1391 parse_command(const char *cmd, const char *param, GString *result) {
1394 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1396 gchar **par = split_quoted(param, TRUE);
1397 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1399 if (c->no_split) { /* don't split */
1400 sharg_append(a, param);
1402 for (i = 0; i < g_strv_length(par); i++)
1403 sharg_append(a, par[i]);
1406 if (result == NULL) {
1407 GString *result_print = g_string_new("");
1409 c->function(uzbl.gui.web_view, a, result_print);
1410 if (result_print->len)
1411 printf("%*s\n", result_print->len, result_print->str);
1413 g_string_free(result_print, TRUE);
1415 c->function(uzbl.gui.web_view, a, result);
1418 g_array_free (a, TRUE);
1421 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1428 if(*uzbl.net.proxy_url == ' '
1429 || uzbl.net.proxy_url == NULL) {
1430 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1431 (GType) SOUP_SESSION_PROXY_URI);
1434 suri = soup_uri_new(uzbl.net.proxy_url);
1435 g_object_set(G_OBJECT(uzbl.net.soup_session),
1436 SOUP_SESSION_PROXY_URI,
1438 soup_uri_free(suri);
1445 if(file_exists(uzbl.gui.icon)) {
1446 if (uzbl.gui.main_window)
1447 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1449 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1451 g_free (uzbl.gui.icon);
1456 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1457 g_array_append_val (a, uzbl.state.uri);
1458 load_uri(uzbl.gui.web_view, a, NULL);
1459 g_array_free (a, TRUE);
1463 cmd_always_insert_mode() {
1464 uzbl.behave.insert_mode =
1465 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1471 g_object_set(G_OBJECT(uzbl.net.soup_session),
1472 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1476 cmd_max_conns_host() {
1477 g_object_set(G_OBJECT(uzbl.net.soup_session),
1478 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1483 soup_session_remove_feature
1484 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1485 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1486 /*g_free(uzbl.net.soup_logger);*/
1488 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1489 soup_session_add_feature(uzbl.net.soup_session,
1490 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1493 static WebKitWebSettings*
1495 return webkit_web_view_get_settings(uzbl.gui.web_view);
1500 WebKitWebSettings *ws = view_settings();
1501 if (uzbl.behave.font_size > 0) {
1502 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1505 if (uzbl.behave.monospace_size > 0) {
1506 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1507 uzbl.behave.monospace_size, NULL);
1509 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1510 uzbl.behave.font_size, NULL);
1516 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1520 cmd_disable_plugins() {
1521 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1522 !uzbl.behave.disable_plugins, NULL);
1526 cmd_disable_scripts() {
1527 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1528 !uzbl.behave.disable_scripts, NULL);
1532 cmd_minimum_font_size() {
1533 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1534 uzbl.behave.minimum_font_size, NULL);
1537 cmd_autoload_img() {
1538 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1539 uzbl.behave.autoload_img, NULL);
1544 cmd_autoshrink_img() {
1545 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1546 uzbl.behave.autoshrink_img, NULL);
1551 cmd_enable_spellcheck() {
1552 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1553 uzbl.behave.enable_spellcheck, NULL);
1557 cmd_enable_private() {
1558 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1559 uzbl.behave.enable_private, NULL);
1564 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1565 uzbl.behave.print_bg, NULL);
1570 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1571 uzbl.behave.style_uri, NULL);
1575 cmd_resizable_txt() {
1576 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1577 uzbl.behave.resizable_txt, NULL);
1581 cmd_default_encoding() {
1582 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1583 uzbl.behave.default_encoding, NULL);
1587 cmd_enforce_96dpi() {
1588 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1589 uzbl.behave.enforce_96dpi, NULL);
1593 cmd_caret_browsing() {
1594 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1595 uzbl.behave.caret_browsing, NULL);
1599 cmd_cookie_handler() {
1600 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1601 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1602 if ((g_strcmp0(split[0], "sh") == 0) ||
1603 (g_strcmp0(split[0], "spawn") == 0)) {
1604 g_free (uzbl.behave.cookie_handler);
1605 uzbl.behave.cookie_handler =
1606 g_strdup_printf("sync_%s %s", split[0], split[1]);
1613 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1618 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1623 if(uzbl.behave.inject_html) {
1624 webkit_web_view_load_html_string (uzbl.gui.web_view,
1625 uzbl.behave.inject_html, NULL);
1634 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1635 uzbl.behave.modmask = 0;
1637 if(uzbl.behave.modkey)
1638 g_free(uzbl.behave.modkey);
1639 uzbl.behave.modkey = buf;
1641 for (i = 0; modkeys[i].key != NULL; i++) {
1642 if (g_strrstr(buf, modkeys[i].key))
1643 uzbl.behave.modmask |= modkeys[i].mask;
1649 if (*uzbl.net.useragent == ' ') {
1650 g_free (uzbl.net.useragent);
1651 uzbl.net.useragent = NULL;
1653 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1655 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1656 g_free(uzbl.net.useragent);
1657 uzbl.net.useragent = ua;
1663 gtk_widget_ref(uzbl.gui.scrolled_win);
1664 gtk_widget_ref(uzbl.gui.mainbar);
1665 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1666 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1668 if(uzbl.behave.status_top) {
1669 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1670 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1673 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1674 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1676 gtk_widget_unref(uzbl.gui.scrolled_win);
1677 gtk_widget_unref(uzbl.gui.mainbar);
1678 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1683 set_var_value(gchar *name, gchar *val) {
1684 uzbl_cmdprop *c = NULL;
1688 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1689 /* check for the variable type */
1690 if (c->type == TYPE_STR) {
1691 buf = expand_vars(val);
1694 } else if(c->type == TYPE_INT) {
1695 int *ip = (int *)c->ptr;
1696 buf = expand_vars(val);
1697 *ip = (int)strtoul(buf, &endp, 10);
1699 } else if (c->type == TYPE_FLOAT) {
1700 float *fp = (float *)c->ptr;
1701 buf = expand_vars(val);
1702 *fp = strtod(buf, &endp);
1706 /* invoke a command specific function */
1707 if(c->func) c->func();
1714 Behaviour *b = &uzbl.behave;
1716 if(b->html_buffer->str) {
1717 webkit_web_view_load_html_string (uzbl.gui.web_view,
1718 b->html_buffer->str, b->base_url);
1719 g_string_free(b->html_buffer, TRUE);
1720 b->html_buffer = g_string_new("");
1724 enum {M_CMD, M_HTML};
1726 parse_cmd_line(const char *ctl_line, GString *result) {
1727 Behaviour *b = &uzbl.behave;
1730 if(b->mode == M_HTML) {
1731 len = strlen(b->html_endmarker);
1732 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1733 if(len == strlen(ctl_line)-1 &&
1734 !strncmp(b->html_endmarker, ctl_line, len)) {
1736 set_var_value("mode", "0");
1741 set_timeout(b->html_timeout);
1742 g_string_append(b->html_buffer, ctl_line);
1745 else if((ctl_line[0] == '#') /* Comments */
1746 || (ctl_line[0] == ' ')
1747 || (ctl_line[0] == '\n'))
1748 ; /* ignore these lines */
1749 else { /* parse a command */
1751 gchar **tokens = NULL;
1752 len = strlen(ctl_line);
1754 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1755 ctlstrip = g_strndup(ctl_line, len - 1);
1756 else ctlstrip = g_strdup(ctl_line);
1758 tokens = g_strsplit(ctlstrip, " ", 2);
1759 parse_command(tokens[0], tokens[1], result);
1766 build_stream_name(int type, const gchar* dir) {
1768 State *s = &uzbl.state;
1771 xwin_str = itos((int)uzbl.xwin);
1773 str = g_strdup_printf
1774 ("%s/uzbl_fifo_%s", dir,
1775 s->instance_name ? s->instance_name : xwin_str);
1776 } else if (type == SOCKET) {
1777 str = g_strdup_printf
1778 ("%s/uzbl_socket_%s", dir,
1779 s->instance_name ? s->instance_name : xwin_str );
1786 control_fifo(GIOChannel *gio, GIOCondition condition) {
1787 if (uzbl.state.verbose)
1788 printf("triggered\n");
1793 if (condition & G_IO_HUP)
1794 g_error ("Fifo: Read end of pipe died!\n");
1797 g_error ("Fifo: GIOChannel broke\n");
1799 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1800 if (ret == G_IO_STATUS_ERROR) {
1801 g_error ("Fifo: Error reading: %s\n", err->message);
1805 parse_cmd_line(ctl_line, NULL);
1812 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1813 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1814 if (unlink(uzbl.comm.fifo_path) == -1)
1815 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1816 g_free(uzbl.comm.fifo_path);
1817 uzbl.comm.fifo_path = NULL;
1820 if (*dir == ' ') { /* space unsets the variable */
1825 GIOChannel *chan = NULL;
1826 GError *error = NULL;
1827 gchar *path = build_stream_name(FIFO, dir);
1829 if (!file_exists(path)) {
1830 if (mkfifo (path, 0666) == 0) {
1831 // 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.
1832 chan = g_io_channel_new_file(path, "r+", &error);
1834 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1835 if (uzbl.state.verbose)
1836 printf ("init_fifo: created successfully as %s\n", path);
1837 uzbl.comm.fifo_path = path;
1839 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1840 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1841 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1842 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1844 /* if we got this far, there was an error; cleanup */
1845 if (error) g_error_free (error);
1852 control_stdin(GIOChannel *gio, GIOCondition condition) {
1854 gchar *ctl_line = NULL;
1857 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1858 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1861 parse_cmd_line(ctl_line, NULL);
1869 GIOChannel *chan = NULL;
1870 GError *error = NULL;
1872 chan = g_io_channel_unix_new(fileno(stdin));
1874 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1875 g_error ("Stdin: could not add watch\n");
1877 if (uzbl.state.verbose)
1878 printf ("Stdin: watch added successfully\n");
1881 g_error ("Stdin: Error while opening: %s\n", error->message);
1883 if (error) g_error_free (error);
1887 control_socket(GIOChannel *chan) {
1888 struct sockaddr_un remote;
1889 unsigned int t = sizeof(remote);
1891 GIOChannel *clientchan;
1893 clientsock = accept (g_io_channel_unix_get_fd(chan),
1894 (struct sockaddr *) &remote, &t);
1896 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1897 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1898 (GIOFunc) control_client_socket, clientchan);
1905 control_client_socket(GIOChannel *clientchan) {
1907 GString *result = g_string_new("");
1908 GError *error = NULL;
1912 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1913 if (ret == G_IO_STATUS_ERROR) {
1914 g_warning ("Error reading: %s\n", error->message);
1915 g_io_channel_shutdown(clientchan, TRUE, &error);
1917 } else if (ret == G_IO_STATUS_EOF) {
1918 /* shutdown and remove channel watch from main loop */
1919 g_io_channel_shutdown(clientchan, TRUE, &error);
1924 parse_cmd_line (ctl_line, result);
1925 g_string_append_c(result, '\n');
1926 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1928 if (ret == G_IO_STATUS_ERROR) {
1929 g_warning ("Error writing: %s", error->message);
1931 g_io_channel_flush(clientchan, &error);
1934 if (error) g_error_free (error);
1935 g_string_free(result, TRUE);
1941 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1942 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1943 if (unlink(uzbl.comm.socket_path) == -1)
1944 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1945 g_free(uzbl.comm.socket_path);
1946 uzbl.comm.socket_path = NULL;
1954 GIOChannel *chan = NULL;
1956 struct sockaddr_un local;
1957 gchar *path = build_stream_name(SOCKET, dir);
1959 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1961 local.sun_family = AF_UNIX;
1962 strcpy (local.sun_path, path);
1963 unlink (local.sun_path);
1965 len = strlen (local.sun_path) + sizeof (local.sun_family);
1966 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1967 if (uzbl.state.verbose)
1968 printf ("init_socket: opened in %s\n", path);
1971 if( (chan = g_io_channel_unix_new(sock)) ) {
1972 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1973 uzbl.comm.socket_path = path;
1976 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1978 /* if we got this far, there was an error; cleanup */
1985 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1986 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1988 // this function may be called very early when the templates are not set (yet), hence the checks
1990 update_title (void) {
1991 Behaviour *b = &uzbl.behave;
1994 if (b->show_status) {
1995 if (b->title_format_short) {
1996 parsed = expand_template(b->title_format_short, FALSE);
1997 if (uzbl.gui.main_window)
1998 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2001 if (b->status_format) {
2002 parsed = expand_template(b->status_format, TRUE);
2003 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2006 if (b->status_background) {
2008 gdk_color_parse (b->status_background, &color);
2009 //labels and hboxes do not draw their own background. applying this on the window is ok as we the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2010 if (uzbl.gui.main_window)
2011 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2014 if (b->title_format_long) {
2015 parsed = expand_template(b->title_format_long, FALSE);
2016 if (uzbl.gui.main_window)
2017 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2024 key_press_cb (GtkWidget* window, GdkEventKey* event)
2026 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2030 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2031 || 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)
2034 /* turn off insert mode (if always_insert_mode is not used) */
2035 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2036 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2041 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2044 if (event->keyval == GDK_Escape) {
2045 g_string_truncate(uzbl.state.keycmd, 0);
2047 dehilight(uzbl.gui.web_view, NULL, NULL);
2051 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2052 if (event->keyval == GDK_Insert) {
2054 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2055 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2057 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2060 g_string_append (uzbl.state.keycmd, str);
2067 if (event->keyval == GDK_BackSpace)
2068 keycmd_bs(NULL, NULL, NULL);
2070 gboolean key_ret = FALSE;
2071 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2073 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2075 run_keycmd(key_ret);
2077 if (key_ret) return (!uzbl.behave.insert_mode);
2082 run_keycmd(const gboolean key_ret) {
2083 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2085 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2086 g_string_truncate(uzbl.state.keycmd, 0);
2087 parse_command(act->name, act->param, NULL);
2091 /* try if it's an incremental keycmd or one that takes args, and run it */
2092 GString* short_keys = g_string_new ("");
2093 GString* short_keys_inc = g_string_new ("");
2095 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2096 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2097 g_string_assign(short_keys_inc, short_keys->str);
2098 g_string_append_c(short_keys, '_');
2099 g_string_append_c(short_keys_inc, '*');
2101 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2102 /* run normal cmds only if return was pressed */
2103 exec_paramcmd(act, i);
2104 g_string_truncate(uzbl.state.keycmd, 0);
2106 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2107 if (key_ret) /* just quit the incremental command on return */
2108 g_string_truncate(uzbl.state.keycmd, 0);
2109 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2113 g_string_truncate(short_keys, short_keys->len - 1);
2115 g_string_free (short_keys, TRUE);
2116 g_string_free (short_keys_inc, TRUE);
2120 exec_paramcmd(const Action *act, const guint i) {
2121 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2122 GString *actionname = g_string_new ("");
2123 GString *actionparam = g_string_new ("");
2124 g_string_erase (parampart, 0, i+1);
2126 g_string_printf (actionname, act->name, parampart->str);
2128 g_string_printf (actionparam, act->param, parampart->str);
2129 parse_command(actionname->str, actionparam->str, NULL);
2130 g_string_free(actionname, TRUE);
2131 g_string_free(actionparam, TRUE);
2132 g_string_free(parampart, TRUE);
2140 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2141 //main_window_ref = g_object_ref(scrolled_window);
2142 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
2144 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2145 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2147 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2148 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2149 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2150 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2151 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2152 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2153 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2154 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2155 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2156 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2157 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2159 return scrolled_window;
2166 g->mainbar = gtk_hbox_new (FALSE, 0);
2168 /* keep a reference to the bar so we can re-pack it at runtime*/
2169 //sbar_ref = g_object_ref(g->mainbar);
2171 g->mainbar_label = gtk_label_new ("");
2172 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2173 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2174 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2175 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2176 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2177 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2182 GtkWidget* create_window () {
2183 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2184 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2185 gtk_widget_set_name (window, "Uzbl browser");
2186 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2187 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2193 GtkPlug* create_plug () {
2194 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2195 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2196 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2203 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2205 If actname is one that calls an external command, this function will inject
2206 newargs in front of the user-provided args in that command line. They will
2207 come become after the body of the script (in sh) or after the name of
2208 the command to execute (in spawn).
2209 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2210 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2212 The return value consist of two strings: the action (sh, ...) and its args.
2214 If act is not one that calls an external command, then the given action merely
2217 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2218 gchar *actdup = g_strdup(actname);
2219 g_array_append_val(rets, actdup);
2221 if ((g_strcmp0(actname, "spawn") == 0) ||
2222 (g_strcmp0(actname, "sh") == 0) ||
2223 (g_strcmp0(actname, "sync_spawn") == 0) ||
2224 (g_strcmp0(actname, "sync_sh") == 0)) {
2226 GString *a = g_string_new("");
2227 gchar **spawnparts = split_quoted(origargs, FALSE);
2228 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2229 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2231 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2232 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2234 g_array_append_val(rets, a->str);
2235 g_string_free(a, FALSE);
2236 g_strfreev(spawnparts);
2238 gchar *origdup = g_strdup(origargs);
2239 g_array_append_val(rets, origdup);
2241 return (gchar**)g_array_free(rets, FALSE);
2245 run_handler (const gchar *act, const gchar *args) {
2246 /* Consider this code a temporary hack to make the handlers usable.
2247 In practice, all this splicing, injection, and reconstruction is
2248 inefficient, annoying and hard to manage. Potential pitfalls arise
2249 when the handler specific args 1) are not quoted (the handler
2250 callbacks should take care of this) 2) are quoted but interfere
2251 with the users' own quotation. A more ideal solution is
2252 to refactor parse_command so that it doesn't just take a string
2253 and execute it; rather than that, we should have a function which
2254 returns the argument vector parsed from the string. This vector
2255 could be modified (e.g. insert additional args into it) before
2256 passing it to the next function that actually executes it. Though
2257 it still isn't perfect for chain actions.. will reconsider & re-
2258 factor when I have the time. -duc */
2260 char **parts = g_strsplit(act, " ", 2);
2262 if (g_strcmp0(parts[0], "chain") == 0) {
2263 GString *newargs = g_string_new("");
2264 gchar **chainparts = split_quoted(parts[1], FALSE);
2266 /* for every argument in the chain, inject the handler args
2267 and make sure the new parts are wrapped in quotes */
2268 gchar **cp = chainparts;
2270 gchar *quotless = NULL;
2271 gchar **spliced_quotless = NULL; // sigh -_-;
2272 gchar **inpart = NULL;
2275 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2277 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2278 } else quotless = g_strdup(*cp);
2280 spliced_quotless = g_strsplit(quotless, " ", 2);
2281 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2282 g_strfreev(spliced_quotless);
2284 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2290 parse_command(parts[0], &(newargs->str[1]), NULL);
2291 g_string_free(newargs, TRUE);
2292 g_strfreev(chainparts);
2295 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2296 parse_command(inparts[0], inparts[1], NULL);
2304 add_binding (const gchar *key, const gchar *act) {
2305 char **parts = g_strsplit(act, " ", 2);
2312 if (uzbl.state.verbose)
2313 printf ("Binding %-10s : %s\n", key, act);
2314 action = new_action(parts[0], parts[1]);
2316 if (g_hash_table_remove (uzbl.bindings, key))
2317 g_warning ("Overwriting existing binding for \"%s\"", key);
2318 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2323 get_xdg_var (XDG_Var xdg) {
2324 const gchar* actual_value = getenv (xdg.environmental);
2325 const gchar* home = getenv ("HOME");
2326 gchar* return_value;
2328 if (! actual_value || strcmp (actual_value, "") == 0) {
2329 if (xdg.default_value) {
2330 return_value = str_replace ("~", home, xdg.default_value);
2332 return_value = NULL;
2335 return_value = str_replace("~", home, actual_value);
2338 return return_value;
2342 find_xdg_file (int xdg_type, char* filename) {
2343 /* xdg_type = 0 => config
2344 xdg_type = 1 => data
2345 xdg_type = 2 => cache*/
2347 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2348 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2351 gchar* temporary_string;
2355 if (! file_exists (temporary_file) && xdg_type != 2) {
2356 buf = get_xdg_var (XDG[3 + xdg_type]);
2357 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2360 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2361 g_free (temporary_file);
2362 temporary_file = g_strconcat (temporary_string, filename, NULL);
2366 //g_free (temporary_string); - segfaults.
2368 if (file_exists (temporary_file)) {
2369 return temporary_file;
2376 State *s = &uzbl.state;
2377 Network *n = &uzbl.net;
2379 for (i = 0; default_config[i].command != NULL; i++) {
2380 parse_cmd_line(default_config[i].command, NULL);
2383 if (g_strcmp0(s->config_file, "-") == 0) {
2384 s->config_file = NULL;
2388 if (!s->config_file) {
2389 s->config_file = find_xdg_file (0, "/uzbl/config");
2392 if (s->config_file) {
2393 GArray* lines = read_file_by_line (s->config_file);
2397 while ((line = g_array_index(lines, gchar*, i))) {
2398 parse_cmd_line (line, NULL);
2402 g_array_free (lines, TRUE);
2404 if (uzbl.state.verbose)
2405 printf ("No configuration file loaded.\n");
2408 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2411 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2414 if (!uzbl.behave.cookie_handler)
2417 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2418 GString *s = g_string_new ("");
2419 SoupURI * soup_uri = soup_message_get_uri(msg);
2420 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2421 run_handler(uzbl.behave.cookie_handler, s->str);
2423 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2424 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2425 if ( p != NULL ) *p = '\0';
2426 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2428 if (uzbl.comm.sync_stdout)
2429 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2431 g_string_free(s, TRUE);
2435 save_cookies (SoupMessage *msg, gpointer user_data){
2439 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2440 cookie = soup_cookie_to_set_cookie_header(ck->data);
2441 SoupURI * soup_uri = soup_message_get_uri(msg);
2442 GString *s = g_string_new ("");
2443 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2444 run_handler(uzbl.behave.cookie_handler, s->str);
2446 g_string_free(s, TRUE);
2451 /* --- WEBINSPECTOR --- */
2453 hide_window_cb(GtkWidget *widget, gpointer data) {
2456 gtk_widget_hide(widget);
2459 static WebKitWebView*
2460 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2463 (void) web_inspector;
2464 GtkWidget* scrolled_window;
2465 GtkWidget* new_web_view;
2468 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2469 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2470 G_CALLBACK(hide_window_cb), NULL);
2472 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2473 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2474 gtk_widget_show(g->inspector_window);
2476 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2477 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2478 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2479 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2480 gtk_widget_show(scrolled_window);
2482 new_web_view = webkit_web_view_new();
2483 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2485 return WEBKIT_WEB_VIEW(new_web_view);
2489 inspector_show_window_cb (WebKitWebInspector* inspector){
2491 gtk_widget_show(uzbl.gui.inspector_window);
2495 /* TODO: Add variables and code to make use of these functions */
2497 inspector_close_window_cb (WebKitWebInspector* inspector){
2503 inspector_attach_window_cb (WebKitWebInspector* inspector){
2509 inspector_detach_window_cb (WebKitWebInspector* inspector){
2515 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2521 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2527 set_up_inspector() {
2529 WebKitWebSettings *settings = view_settings();
2530 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2532 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2533 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2534 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2535 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2536 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2537 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2538 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2540 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2544 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2546 uzbl_cmdprop *c = v;
2551 if(c->type == TYPE_STR)
2552 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2553 else if(c->type == TYPE_INT)
2554 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2558 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2562 printf("bind %s = %s %s\n", (char *)k ,
2563 (char *)a->name, a->param?(char *)a->param:"");
2568 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2569 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2574 main (int argc, char* argv[]) {
2575 gtk_init (&argc, &argv);
2576 if (!g_thread_supported ())
2577 g_thread_init (NULL);
2578 uzbl.state.executable_path = g_strdup(argv[0]);
2579 uzbl.state.selected_url = NULL;
2580 uzbl.state.searchtx = NULL;
2582 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2583 g_option_context_add_main_entries (context, entries, NULL);
2584 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2585 g_option_context_parse (context, &argc, &argv, NULL);
2586 g_option_context_free(context);
2588 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2589 gboolean verbose_override = uzbl.state.verbose;
2591 /* initialize hash table */
2592 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2594 uzbl.net.soup_session = webkit_get_default_session();
2595 uzbl.state.keycmd = g_string_new("");
2597 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2598 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2599 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2600 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2601 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2602 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2605 if(uname(&uzbl.state.unameinfo) == -1)
2606 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2608 uzbl.gui.sbar.progress_s = g_strdup("=");
2609 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2610 uzbl.gui.sbar.progress_w = 10;
2612 /* HTML mode defaults*/
2613 uzbl.behave.html_buffer = g_string_new("");
2614 uzbl.behave.html_endmarker = g_strdup(".");
2615 uzbl.behave.html_timeout = 60;
2616 uzbl.behave.base_url = g_strdup("http://invalid");
2618 /* default mode indicators */
2619 uzbl.behave.insert_indicator = g_strdup("I");
2620 uzbl.behave.cmd_indicator = g_strdup("C");
2624 make_var_to_name_hash();
2626 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2628 uzbl.gui.scrolled_win = create_browser();
2631 /* initial packing */
2632 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2633 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2635 if (uzbl.state.socket_id) {
2636 uzbl.gui.plug = create_plug ();
2637 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2638 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2640 uzbl.gui.main_window = create_window ();
2641 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2642 gtk_widget_show_all (uzbl.gui.main_window);
2643 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2646 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2648 if (uzbl.state.verbose) {
2649 printf("Uzbl start location: %s\n", argv[0]);
2650 if (uzbl.state.socket_id)
2651 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2653 printf("window_id %i\n",(int) uzbl.xwin);
2654 printf("pid %i\n", getpid ());
2655 printf("name: %s\n", uzbl.state.instance_name);
2658 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2659 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2660 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2661 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2662 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2666 if (!uzbl.behave.show_status)
2667 gtk_widget_hide(uzbl.gui.mainbar);
2674 if (verbose_override > uzbl.state.verbose)
2675 uzbl.state.verbose = verbose_override;
2678 set_var_value("uri", uri_override);
2679 g_free(uri_override);
2680 } else if (uzbl.state.uri)
2681 cmd_load_uri(uzbl.gui.web_view, NULL);
2686 return EXIT_SUCCESS;
2689 /* vi: set et ts=4: */