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 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
133 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
134 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
135 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
136 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
137 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
138 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
139 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
140 /* exported WebKitWebSettings properties */
141 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
142 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
143 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
144 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
145 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
146 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
147 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
148 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
149 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
150 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
151 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
152 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
153 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
154 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
155 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
156 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
158 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
159 }, *n2v_p = var_name_to_ptr;
165 { "SHIFT", GDK_SHIFT_MASK }, // shift
166 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
167 { "CONTROL", GDK_CONTROL_MASK }, // control
168 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
169 { "MOD2", GDK_MOD2_MASK }, // 5th mod
170 { "MOD3", GDK_MOD3_MASK }, // 6th mod
171 { "MOD4", GDK_MOD4_MASK }, // 7th mod
172 { "MOD5", GDK_MOD5_MASK }, // 8th mod
173 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
174 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
175 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
176 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
177 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
178 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
179 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
180 { "META", GDK_META_MASK }, // meta (since 2.10)
185 /* construct a hash from the var_name_to_ptr array for quick access */
187 make_var_to_name_hash() {
188 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
190 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
195 /* --- UTILITY FUNCTIONS --- */
197 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
199 get_exp_type(gchar *s) {
203 else if(*(s+1) == '{')
204 return EXP_BRACED_VAR;
205 else if(*(s+1) == '<')
208 return EXP_SIMPLE_VAR;
214 * recurse == 1: don't expand '@(command)@'
215 * recurse == 2: don't expand '@<java script>@'
218 expand(char *s, guint recurse) {
222 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
227 gchar *cmd_stdout = NULL;
229 GString *buf = g_string_new("");
230 GString *js_ret = g_string_new("");
235 g_string_append_c(buf, *++s);
240 etype = get_exp_type(s);
245 if( (vend = strpbrk(s, end_simple_var)) ||
246 (vend = strchr(s, '\0')) ) {
247 strncpy(ret, s, vend-s);
253 if( (vend = strchr(s, upto)) ||
254 (vend = strchr(s, '\0')) ) {
255 strncpy(ret, s, vend-s);
261 strcpy(str_end, ")@");
263 if( (vend = strstr(s, str_end)) ||
264 (vend = strchr(s, '\0')) ) {
265 strncpy(ret, s, vend-s);
271 strcpy(str_end, ">@");
273 if( (vend = strstr(s, str_end)) ||
274 (vend = strchr(s, '\0')) ) {
275 strncpy(ret, s, vend-s);
281 if(etype == EXP_SIMPLE_VAR ||
282 etype == EXP_BRACED_VAR) {
283 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
284 if(c->type == TYPE_STR)
285 g_string_append(buf, (gchar *)*c->ptr);
286 else if(c->type == TYPE_INT) {
287 char *b = itos((int)*c->ptr);
288 g_string_append(buf, b);
292 if(etype == EXP_SIMPLE_VAR)
297 else if(recurse != 1 &&
299 mycmd = expand(ret, 1);
300 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
304 g_printerr("error on running command: %s\n", err->message);
307 else if (*cmd_stdout) {
308 g_string_append(buf, cmd_stdout);
313 else if(recurse != 2 &&
315 mycmd = expand(ret, 2);
316 eval_js(uzbl.gui.web_view, mycmd, js_ret);
320 g_string_append(buf, js_ret->str);
321 g_string_free(js_ret, TRUE);
322 js_ret = g_string_new("");
329 g_string_append_c(buf, *s);
334 g_string_free(js_ret, TRUE);
335 return g_string_free(buf, FALSE);
342 snprintf(tmp, sizeof(tmp), "%i", val);
343 return g_strdup(tmp);
347 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
350 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
353 str_replace (const char* search, const char* replace, const char* string) {
357 buf = g_strsplit (string, search, -1);
358 ret = g_strjoinv (replace, buf);
359 g_strfreev(buf); // somebody said this segfaults
365 read_file_by_line (gchar *path) {
366 GIOChannel *chan = NULL;
367 gchar *readbuf = NULL;
369 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
372 chan = g_io_channel_new_file(path, "r", NULL);
375 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
376 const gchar* val = g_strdup (readbuf);
377 g_array_append_val (lines, val);
382 g_io_channel_unref (chan);
384 fprintf(stderr, "File '%s' not be read.\n", path);
391 gchar* parseenv (char* string) {
392 extern char** environ;
393 gchar* tmpstr = NULL;
397 while (environ[i] != NULL) {
398 gchar** env = g_strsplit (environ[i], "=", 2);
399 gchar* envname = g_strconcat ("$", env[0], NULL);
401 if (g_strrstr (string, envname) != NULL) {
402 tmpstr = g_strdup(string);
404 string = str_replace(envname, env[1], tmpstr);
409 g_strfreev (env); // somebody said this breaks uzbl
417 setup_signal(int signr, sigfunc *shandler) {
418 struct sigaction nh, oh;
420 nh.sa_handler = shandler;
421 sigemptyset(&nh.sa_mask);
424 if(sigaction(signr, &nh, &oh) < 0)
432 if (uzbl.behave.fifo_dir)
433 unlink (uzbl.comm.fifo_path);
434 if (uzbl.behave.socket_dir)
435 unlink (uzbl.comm.socket_path);
437 g_free(uzbl.state.executable_path);
438 g_string_free(uzbl.state.keycmd, TRUE);
439 g_hash_table_destroy(uzbl.bindings);
440 g_hash_table_destroy(uzbl.behave.commands);
443 /* used for html_mode_timeout
444 * be sure to extend this function to use
445 * more timers if needed in other places
448 set_timeout(int seconds) {
450 memset(&t, 0, sizeof t);
452 t.it_value.tv_sec = seconds;
453 t.it_value.tv_usec = 0;
454 setitimer(ITIMER_REAL, &t, NULL);
457 /* --- SIGNAL HANDLER --- */
460 catch_sigterm(int s) {
466 catch_sigint(int s) {
476 set_var_value("mode", "0");
481 /* --- CALLBACKS --- */
484 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
487 (void) navigation_action;
488 (void) policy_decision;
490 const gchar* uri = webkit_network_request_get_uri (request);
491 if (uzbl.state.verbose)
492 printf("New window requested -> %s \n", uri);
493 webkit_web_policy_decision_use(policy_decision);
498 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
503 /* If we can display it, let's display it... */
504 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
505 webkit_web_policy_decision_use (policy_decision);
509 /* ...everything we can't displayed is downloaded */
510 webkit_web_policy_decision_download (policy_decision);
515 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
519 if (uzbl.state.selected_url != NULL) {
520 if (uzbl.state.verbose)
521 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
522 new_window_load_uri(uzbl.state.selected_url);
524 if (uzbl.state.verbose)
525 printf("New web view -> %s\n","Nothing to open, exiting");
531 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
534 if (uzbl.behave.download_handler) {
535 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
536 if (uzbl.state.verbose)
537 printf("Download -> %s\n",uri);
538 /* if urls not escaped, we may have to escape and quote uri before this call */
539 run_handler(uzbl.behave.download_handler, uri);
544 /* scroll a bar in a given direction */
546 scroll (GtkAdjustment* bar, GArray *argv) {
550 gdouble page_size = gtk_adjustment_get_page_size(bar);
551 gdouble value = gtk_adjustment_get_value(bar);
552 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
555 value += page_size * amount * 0.01;
559 max_value = gtk_adjustment_get_upper(bar) - page_size;
561 if (value > max_value)
562 value = max_value; /* don't scroll past the end of the page */
564 gtk_adjustment_set_value (bar, value);
568 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
569 (void) page; (void) argv; (void) result;
570 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
574 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
575 (void) page; (void) argv; (void) result;
576 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
577 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
581 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
582 (void) page; (void) result;
583 scroll(uzbl.gui.bar_v, argv);
587 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
588 (void) page; (void) result;
589 scroll(uzbl.gui.bar_h, argv);
594 if (!uzbl.behave.show_status) {
595 gtk_widget_hide(uzbl.gui.mainbar);
597 gtk_widget_show(uzbl.gui.mainbar);
603 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
608 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
612 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
617 if (uzbl.behave.show_status) {
618 gtk_widget_hide(uzbl.gui.mainbar);
620 gtk_widget_show(uzbl.gui.mainbar);
622 uzbl.behave.show_status = !uzbl.behave.show_status;
627 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
631 //Set selected_url state variable
632 g_free(uzbl.state.selected_url);
633 uzbl.state.selected_url = NULL;
635 uzbl.state.selected_url = g_strdup(link);
641 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
644 const gchar *title = webkit_web_view_get_title(web_view);
645 if (uzbl.gui.main_title)
646 g_free (uzbl.gui.main_title);
647 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
652 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
655 uzbl.gui.sbar.load_progress = progress;
660 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
664 if (uzbl.behave.load_finish_handler)
665 run_handler(uzbl.behave.load_finish_handler, "");
669 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
673 uzbl.gui.sbar.load_progress = 0;
674 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
675 if (uzbl.behave.load_start_handler)
676 run_handler(uzbl.behave.load_start_handler, "");
680 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
683 g_free (uzbl.state.uri);
684 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
685 uzbl.state.uri = g_string_free (newuri, FALSE);
686 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
687 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
690 if (uzbl.behave.load_commit_handler)
691 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
695 destroy_cb (GtkWidget* widget, gpointer data) {
703 if (uzbl.behave.history_handler) {
705 struct tm * timeinfo;
708 timeinfo = localtime ( &rawtime );
709 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
710 run_handler(uzbl.behave.history_handler, date);
715 /* VIEW funcs (little webkit wrappers) */
716 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
718 VIEWFUNC(reload_bypass_cache)
719 VIEWFUNC(stop_loading)
726 /* -- command to callback/function map for things we cannot attach to any signals */
727 static struct {char *key; CommandInfo value;} cmdlist[] =
728 { /* key function no_split */
729 { "back", {view_go_back, 0} },
730 { "forward", {view_go_forward, 0} },
731 { "scroll_vert", {scroll_vert, 0} },
732 { "scroll_horz", {scroll_horz, 0} },
733 { "scroll_begin", {scroll_begin, 0} },
734 { "scroll_end", {scroll_end, 0} },
735 { "reload", {view_reload, 0}, },
736 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
737 { "stop", {view_stop_loading, 0}, },
738 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
739 { "zoom_out", {view_zoom_out, 0}, },
740 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
741 { "uri", {load_uri, TRUE} },
742 { "js", {run_js, TRUE} },
743 { "script", {run_external_js, 0} },
744 { "toggle_status", {toggle_status_cb, 0} },
745 { "spawn", {spawn, 0} },
746 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
747 { "sh", {spawn_sh, 0} },
748 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
749 { "exit", {close_uzbl, 0} },
750 { "search", {search_forward_text, TRUE} },
751 { "search_reverse", {search_reverse_text, TRUE} },
752 { "dehilight", {dehilight, 0} },
753 { "toggle_insert_mode", {toggle_insert_mode, 0} },
754 { "set", {set_var, TRUE} },
755 //{ "get", {get_var, TRUE} },
756 { "bind", {act_bind, TRUE} },
757 { "dump_config", {act_dump_config, 0} },
758 { "keycmd", {keycmd, TRUE} },
759 { "keycmd_nl", {keycmd_nl, TRUE} },
760 { "keycmd_bs", {keycmd_bs, 0} },
761 { "chain", {chain, 0} },
762 { "print", {print, TRUE} }
769 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
771 for (i = 0; i < LENGTH(cmdlist); i++)
772 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
775 /* -- CORE FUNCTIONS -- */
778 free_action(gpointer act) {
779 Action *action = (Action*)act;
780 g_free(action->name);
782 g_free(action->param);
787 new_action(const gchar *name, const gchar *param) {
788 Action *action = g_new(Action, 1);
790 action->name = g_strdup(name);
792 action->param = g_strdup(param);
794 action->param = NULL;
800 file_exists (const char * filename) {
801 return (access(filename, F_OK) == 0);
805 set_var(WebKitWebView *page, GArray *argv, GString *result) {
806 (void) page; (void) result;
807 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
808 if (split[0] != NULL) {
809 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
810 set_var_value(g_strstrip(split[0]), value);
817 print(WebKitWebView *page, GArray *argv, GString *result) {
818 (void) page; (void) result;
821 buf = expand(argv_idx(argv, 0), 0);
822 g_string_assign(result, buf);
827 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
828 (void) page; (void) result;
829 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
830 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
831 add_binding(g_strstrip(split[0]), value);
843 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
844 (void) page; (void) result;
846 if (argv_idx(argv, 0)) {
847 if (strcmp (argv_idx(argv, 0), "0") == 0) {
848 uzbl.behave.insert_mode = FALSE;
850 uzbl.behave.insert_mode = TRUE;
853 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
860 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
863 if (argv_idx(argv, 0)) {
864 GString* newuri = g_string_new (argv_idx(argv, 0));
865 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
866 run_js(web_view, argv, NULL);
869 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
870 g_string_prepend (newuri, "http://");
871 /* if we do handle cookies, ask our handler for them */
872 webkit_web_view_load_uri (web_view, newuri->str);
873 g_string_free (newuri, TRUE);
881 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
882 size_t argumentCount, const JSValueRef arguments[],
883 JSValueRef* exception) {
888 JSStringRef js_result_string;
889 GString *result = g_string_new("");
891 if (argumentCount >= 1) {
892 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
893 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
894 char ctl_line[arg_size];
895 JSStringGetUTF8CString(arg, ctl_line, arg_size);
897 parse_cmd_line(ctl_line, result);
899 JSStringRelease(arg);
901 js_result_string = JSStringCreateWithUTF8CString(result->str);
903 g_string_free(result, TRUE);
905 return JSValueMakeString(ctx, js_result_string);
908 static JSStaticFunction js_static_functions[] = {
909 {"run", js_run_command, kJSPropertyAttributeNone},
914 /* This function creates the class and its definition, only once */
915 if (!uzbl.js.initialized) {
916 /* it would be pretty cool to make this dynamic */
917 uzbl.js.classdef = kJSClassDefinitionEmpty;
918 uzbl.js.classdef.staticFunctions = js_static_functions;
920 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
926 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
927 WebKitWebFrame *frame;
928 JSGlobalContextRef context;
929 JSObjectRef globalobject;
930 JSStringRef var_name;
932 JSStringRef js_script;
933 JSValueRef js_result;
934 JSStringRef js_result_string;
935 size_t js_result_size;
939 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
940 context = webkit_web_frame_get_global_context(frame);
941 globalobject = JSContextGetGlobalObject(context);
943 /* uzbl javascript namespace */
944 var_name = JSStringCreateWithUTF8CString("Uzbl");
945 JSObjectSetProperty(context, globalobject, var_name,
946 JSObjectMake(context, uzbl.js.classref, NULL),
947 kJSClassAttributeNone, NULL);
949 /* evaluate the script and get return value*/
950 js_script = JSStringCreateWithUTF8CString(script);
951 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
952 if (js_result && !JSValueIsUndefined(context, js_result)) {
953 js_result_string = JSValueToStringCopy(context, js_result, NULL);
954 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
956 if (js_result_size) {
957 char js_result_utf8[js_result_size];
958 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
959 g_string_assign(result, js_result_utf8);
962 JSStringRelease(js_result_string);
966 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
968 JSStringRelease(var_name);
969 JSStringRelease(js_script);
973 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
975 if (argv_idx(argv, 0))
976 eval_js(web_view, argv_idx(argv, 0), result);
980 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
982 if (argv_idx(argv, 0)) {
983 GArray* lines = read_file_by_line (argv_idx (argv, 0));
988 while ((line = g_array_index(lines, gchar*, i))) {
990 js = g_strdup (line);
992 gchar* newjs = g_strconcat (js, line, NULL);
999 if (uzbl.state.verbose)
1000 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1002 if (argv_idx (argv, 1)) {
1003 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1007 eval_js (web_view, js, result);
1009 g_array_free (lines, TRUE);
1014 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1015 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1016 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1017 webkit_web_view_unmark_text_matches (page);
1018 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1019 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1023 if (uzbl.state.searchtx) {
1024 if (uzbl.state.verbose)
1025 printf ("Searching: %s\n", uzbl.state.searchtx);
1026 webkit_web_view_set_highlight_text_matches (page, TRUE);
1027 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1032 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1034 search_text(page, argv, TRUE);
1038 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1040 search_text(page, argv, FALSE);
1044 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1045 (void) argv; (void) result;
1046 webkit_web_view_set_highlight_text_matches (page, FALSE);
1051 new_window_load_uri (const gchar * uri) {
1052 GString* to_execute = g_string_new ("");
1053 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1055 for (i = 0; entries[i].long_name != NULL; i++) {
1056 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1057 gchar** str = (gchar**)entries[i].arg_data;
1059 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1063 if (uzbl.state.verbose)
1064 printf("\n%s\n", to_execute->str);
1065 g_spawn_command_line_async (to_execute->str, NULL);
1066 g_string_free (to_execute, TRUE);
1070 chain (WebKitWebView *page, GArray *argv, GString *result) {
1071 (void) page; (void) result;
1073 gchar **parts = NULL;
1075 while ((a = argv_idx(argv, i++))) {
1076 parts = g_strsplit (a, " ", 2);
1077 parse_command(parts[0], parts[1], result);
1083 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1087 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1093 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1097 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1103 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1108 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1110 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1115 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1122 /* --Statusbar functions-- */
1124 build_progressbar_ascii(int percent) {
1125 int width=uzbl.gui.sbar.progress_w;
1128 GString *bar = g_string_new("");
1130 l = (double)percent*((double)width/100.);
1131 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1133 for(i=0; i<(int)l; i++)
1134 g_string_append(bar, uzbl.gui.sbar.progress_s);
1137 g_string_append(bar, uzbl.gui.sbar.progress_u);
1139 return g_string_free(bar, FALSE);
1144 const GScannerConfig scan_config = {
1147 ) /* cset_skip_characters */,
1152 ) /* cset_identifier_first */,
1159 ) /* cset_identifier_nth */,
1160 ( "" ) /* cpair_comment_single */,
1162 TRUE /* case_sensitive */,
1164 FALSE /* skip_comment_multi */,
1165 FALSE /* skip_comment_single */,
1166 FALSE /* scan_comment_multi */,
1167 TRUE /* scan_identifier */,
1168 TRUE /* scan_identifier_1char */,
1169 FALSE /* scan_identifier_NULL */,
1170 TRUE /* scan_symbols */,
1171 FALSE /* scan_binary */,
1172 FALSE /* scan_octal */,
1173 FALSE /* scan_float */,
1174 FALSE /* scan_hex */,
1175 FALSE /* scan_hex_dollar */,
1176 FALSE /* scan_string_sq */,
1177 FALSE /* scan_string_dq */,
1178 TRUE /* numbers_2_int */,
1179 FALSE /* int_2_float */,
1180 FALSE /* identifier_2_string */,
1181 FALSE /* char_2_token */,
1182 FALSE /* symbol_2_token */,
1183 TRUE /* scope_0_fallback */,
1188 uzbl.scan = g_scanner_new(&scan_config);
1189 while(symp->symbol_name) {
1190 g_scanner_scope_add_symbol(uzbl.scan, 0,
1192 GINT_TO_POINTER(symp->symbol_token));
1198 expand_template(const char *template, gboolean escape_markup) {
1199 if(!template) return NULL;
1201 GTokenType token = G_TOKEN_NONE;
1202 GString *ret = g_string_new("");
1206 g_scanner_input_text(uzbl.scan, template, strlen(template));
1207 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1208 token = g_scanner_get_next_token(uzbl.scan);
1210 if(token == G_TOKEN_SYMBOL) {
1211 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1215 buf = uzbl.state.uri?
1216 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1217 g_string_append(ret, buf);
1221 g_string_append(ret, uzbl.state.uri?
1222 uzbl.state.uri:g_strdup(""));
1225 buf = itos(uzbl.gui.sbar.load_progress);
1226 g_string_append(ret, buf);
1229 case SYM_LOADPRGSBAR:
1230 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1231 g_string_append(ret, buf);
1236 buf = uzbl.gui.main_title?
1237 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1238 g_string_append(ret, buf);
1242 g_string_append(ret, uzbl.gui.main_title?
1243 uzbl.gui.main_title:g_strdup(""));
1245 case SYM_SELECTED_URI:
1247 buf = uzbl.state.selected_url?
1248 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1249 g_string_append(ret, buf);
1253 g_string_append(ret, uzbl.state.selected_url?
1254 uzbl.state.selected_url:g_strdup(""));
1257 buf = itos(uzbl.xwin);
1258 g_string_append(ret,
1259 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1264 buf = uzbl.state.keycmd->str?
1265 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1266 g_string_append(ret, buf);
1270 g_string_append(ret, uzbl.state.keycmd->str?
1271 uzbl.state.keycmd->str:g_strdup(""));
1274 g_string_append(ret,
1275 uzbl.behave.insert_mode?
1276 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1279 g_string_append(ret,
1280 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1282 /* useragent syms */
1284 buf = itos(WEBKIT_MAJOR_VERSION);
1285 g_string_append(ret, buf);
1289 buf = itos(WEBKIT_MINOR_VERSION);
1290 g_string_append(ret, buf);
1294 buf = itos(WEBKIT_MICRO_VERSION);
1295 g_string_append(ret, buf);
1299 g_string_append(ret, uzbl.state.unameinfo.sysname);
1302 g_string_append(ret, uzbl.state.unameinfo.nodename);
1305 g_string_append(ret, uzbl.state.unameinfo.release);
1308 g_string_append(ret, uzbl.state.unameinfo.version);
1311 g_string_append(ret, uzbl.state.unameinfo.machine);
1314 g_string_append(ret, ARCH);
1317 case SYM_DOMAINNAME:
1318 g_string_append(ret, uzbl.state.unameinfo.domainname);
1322 g_string_append(ret, COMMIT);
1328 else if(token == G_TOKEN_INT) {
1329 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1330 g_string_append(ret, buf);
1333 else if(token == G_TOKEN_IDENTIFIER) {
1334 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1336 else if(token == G_TOKEN_CHAR) {
1337 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1341 return g_string_free(ret, FALSE);
1343 /* --End Statusbar functions-- */
1346 sharg_append(GArray *a, const gchar *str) {
1347 const gchar *s = (str ? str : "");
1348 g_array_append_val(a, s);
1351 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1353 run_command (const gchar *command, const guint npre, const gchar **args,
1354 const gboolean sync, char **output_stdout) {
1355 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1358 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1359 gchar *pid = itos(getpid());
1360 gchar *xwin = itos(uzbl.xwin);
1362 sharg_append(a, command);
1363 for (i = 0; i < npre; i++) /* add n args before the default vars */
1364 sharg_append(a, args[i]);
1365 sharg_append(a, uzbl.state.config_file);
1366 sharg_append(a, pid);
1367 sharg_append(a, xwin);
1368 sharg_append(a, uzbl.comm.fifo_path);
1369 sharg_append(a, uzbl.comm.socket_path);
1370 sharg_append(a, uzbl.state.uri);
1371 sharg_append(a, uzbl.gui.main_title);
1373 for (i = npre; i < g_strv_length((gchar**)args); i++)
1374 sharg_append(a, args[i]);
1378 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1380 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1381 NULL, NULL, output_stdout, NULL, NULL, &err);
1382 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1383 NULL, NULL, NULL, &err);
1385 if (uzbl.state.verbose) {
1386 GString *s = g_string_new("spawned:");
1387 for (i = 0; i < (a->len); i++) {
1388 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1389 g_string_append_printf(s, " %s", qarg);
1392 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1393 printf("%s\n", s->str);
1394 g_string_free(s, TRUE);
1396 printf("Stdout: %s\n", *output_stdout);
1400 g_printerr("error on run_command: %s\n", err->message);
1405 g_array_free (a, TRUE);
1410 split_quoted(const gchar* src, const gboolean unquote) {
1411 /* split on unquoted space, return array of strings;
1412 remove a layer of quotes and backslashes if unquote */
1413 if (!src) return NULL;
1415 gboolean dq = FALSE;
1416 gboolean sq = FALSE;
1417 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1418 GString *s = g_string_new ("");
1422 for (p = src; *p != '\0'; p++) {
1423 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1424 else if (*p == '\\') { g_string_append_c(s, *p++);
1425 g_string_append_c(s, *p); }
1426 else if ((*p == '"') && unquote && !sq) dq = !dq;
1427 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1429 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1430 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1432 else if ((*p == ' ') && !dq && !sq) {
1433 dup = g_strdup(s->str);
1434 g_array_append_val(a, dup);
1435 g_string_truncate(s, 0);
1436 } else g_string_append_c(s, *p);
1438 dup = g_strdup(s->str);
1439 g_array_append_val(a, dup);
1440 ret = (gchar**)a->data;
1441 g_array_free (a, FALSE);
1442 g_string_free (s, TRUE);
1447 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1448 (void)web_view; (void)result;
1449 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1450 if (argv_idx(argv, 0))
1451 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1455 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1456 (void)web_view; (void)result;
1458 if (argv_idx(argv, 0))
1459 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1460 TRUE, &uzbl.comm.sync_stdout);
1464 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1465 (void)web_view; (void)result;
1466 if (!uzbl.behave.shell_cmd) {
1467 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1472 gchar *spacer = g_strdup("");
1473 g_array_insert_val(argv, 1, spacer);
1474 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1476 for (i = 1; i < g_strv_length(cmd); i++)
1477 g_array_prepend_val(argv, cmd[i]);
1479 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1485 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1486 (void)web_view; (void)result;
1487 if (!uzbl.behave.shell_cmd) {
1488 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1493 gchar *spacer = g_strdup("");
1494 g_array_insert_val(argv, 1, spacer);
1495 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1497 for (i = 1; i < g_strv_length(cmd); i++)
1498 g_array_prepend_val(argv, cmd[i]);
1500 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1501 TRUE, &uzbl.comm.sync_stdout);
1507 parse_command(const char *cmd, const char *param, GString *result) {
1510 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1512 gchar **par = split_quoted(param, TRUE);
1513 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1515 if (c->no_split) { /* don't split */
1516 sharg_append(a, param);
1518 for (i = 0; i < g_strv_length(par); i++)
1519 sharg_append(a, par[i]);
1522 if (result == NULL) {
1523 GString *result_print = g_string_new("");
1525 c->function(uzbl.gui.web_view, a, result_print);
1526 if (result_print->len)
1527 printf("%*s\n", result_print->len, result_print->str);
1529 g_string_free(result_print, TRUE);
1531 c->function(uzbl.gui.web_view, a, result);
1534 g_array_free (a, TRUE);
1537 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1544 if(*uzbl.net.proxy_url == ' '
1545 || uzbl.net.proxy_url == NULL) {
1546 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1547 (GType) SOUP_SESSION_PROXY_URI);
1550 suri = soup_uri_new(uzbl.net.proxy_url);
1551 g_object_set(G_OBJECT(uzbl.net.soup_session),
1552 SOUP_SESSION_PROXY_URI,
1554 soup_uri_free(suri);
1561 if(file_exists(uzbl.gui.icon)) {
1562 if (uzbl.gui.main_window)
1563 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1565 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1571 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1572 g_array_append_val (a, uzbl.state.uri);
1573 load_uri(uzbl.gui.web_view, a, NULL);
1574 g_array_free (a, TRUE);
1578 cmd_always_insert_mode() {
1579 uzbl.behave.insert_mode =
1580 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1586 g_object_set(G_OBJECT(uzbl.net.soup_session),
1587 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1591 cmd_max_conns_host() {
1592 g_object_set(G_OBJECT(uzbl.net.soup_session),
1593 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1598 soup_session_remove_feature
1599 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1600 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1601 /*g_free(uzbl.net.soup_logger);*/
1603 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1604 soup_session_add_feature(uzbl.net.soup_session,
1605 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1608 static WebKitWebSettings*
1610 return webkit_web_view_get_settings(uzbl.gui.web_view);
1615 WebKitWebSettings *ws = view_settings();
1616 if (uzbl.behave.font_size > 0) {
1617 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1620 if (uzbl.behave.monospace_size > 0) {
1621 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1622 uzbl.behave.monospace_size, NULL);
1624 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1625 uzbl.behave.font_size, NULL);
1631 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1635 cmd_disable_plugins() {
1636 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1637 !uzbl.behave.disable_plugins, NULL);
1641 cmd_disable_scripts() {
1642 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1643 !uzbl.behave.disable_scripts, NULL);
1647 cmd_minimum_font_size() {
1648 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1649 uzbl.behave.minimum_font_size, NULL);
1652 cmd_autoload_img() {
1653 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1654 uzbl.behave.autoload_img, NULL);
1659 cmd_autoshrink_img() {
1660 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1661 uzbl.behave.autoshrink_img, NULL);
1666 cmd_enable_spellcheck() {
1667 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1668 uzbl.behave.enable_spellcheck, NULL);
1672 cmd_enable_private() {
1673 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1674 uzbl.behave.enable_private, NULL);
1679 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1680 uzbl.behave.print_bg, NULL);
1685 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1686 uzbl.behave.style_uri, NULL);
1690 cmd_resizable_txt() {
1691 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1692 uzbl.behave.resizable_txt, NULL);
1696 cmd_default_encoding() {
1697 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1698 uzbl.behave.default_encoding, NULL);
1702 cmd_enforce_96dpi() {
1703 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1704 uzbl.behave.enforce_96dpi, NULL);
1708 cmd_caret_browsing() {
1709 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1710 uzbl.behave.caret_browsing, NULL);
1714 cmd_cookie_handler() {
1715 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1716 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1717 if ((g_strcmp0(split[0], "sh") == 0) ||
1718 (g_strcmp0(split[0], "spawn") == 0)) {
1719 g_free (uzbl.behave.cookie_handler);
1720 uzbl.behave.cookie_handler =
1721 g_strdup_printf("sync_%s %s", split[0], split[1]);
1728 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1733 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1738 if(uzbl.behave.inject_html) {
1739 webkit_web_view_load_html_string (uzbl.gui.web_view,
1740 uzbl.behave.inject_html, NULL);
1749 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1750 uzbl.behave.modmask = 0;
1752 if(uzbl.behave.modkey)
1753 g_free(uzbl.behave.modkey);
1754 uzbl.behave.modkey = buf;
1756 for (i = 0; modkeys[i].key != NULL; i++) {
1757 if (g_strrstr(buf, modkeys[i].key))
1758 uzbl.behave.modmask |= modkeys[i].mask;
1764 if (*uzbl.net.useragent == ' ') {
1765 g_free (uzbl.net.useragent);
1766 uzbl.net.useragent = NULL;
1768 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1770 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1771 g_free(uzbl.net.useragent);
1772 uzbl.net.useragent = ua;
1778 gtk_widget_ref(uzbl.gui.scrolled_win);
1779 gtk_widget_ref(uzbl.gui.mainbar);
1780 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1781 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1783 if(uzbl.behave.status_top) {
1784 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1785 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1788 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1789 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1791 gtk_widget_unref(uzbl.gui.scrolled_win);
1792 gtk_widget_unref(uzbl.gui.mainbar);
1793 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1798 set_var_value(gchar *name, gchar *val) {
1799 uzbl_cmdprop *c = NULL;
1803 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1804 /* check for the variable type */
1805 if (c->type == TYPE_STR) {
1806 buf = expand(val, 0);
1809 } else if(c->type == TYPE_INT) {
1810 int *ip = (int *)c->ptr;
1811 buf = expand(val, 0);
1812 *ip = (int)strtoul(buf, &endp, 10);
1814 } else if (c->type == TYPE_FLOAT) {
1815 float *fp = (float *)c->ptr;
1816 buf = expand(val, 0);
1817 *fp = strtod(buf, &endp);
1821 /* invoke a command specific function */
1822 if(c->func) c->func();
1829 Behaviour *b = &uzbl.behave;
1831 if(b->html_buffer->str) {
1832 webkit_web_view_load_html_string (uzbl.gui.web_view,
1833 b->html_buffer->str, b->base_url);
1834 g_string_free(b->html_buffer, TRUE);
1835 b->html_buffer = g_string_new("");
1839 enum {M_CMD, M_HTML};
1841 parse_cmd_line(const char *ctl_line, GString *result) {
1842 Behaviour *b = &uzbl.behave;
1845 if(b->mode == M_HTML) {
1846 len = strlen(b->html_endmarker);
1847 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1848 if(len == strlen(ctl_line)-1 &&
1849 !strncmp(b->html_endmarker, ctl_line, len)) {
1851 set_var_value("mode", "0");
1856 set_timeout(b->html_timeout);
1857 g_string_append(b->html_buffer, ctl_line);
1860 else if((ctl_line[0] == '#') /* Comments */
1861 || (ctl_line[0] == ' ')
1862 || (ctl_line[0] == '\n'))
1863 ; /* ignore these lines */
1864 else { /* parse a command */
1866 gchar **tokens = NULL;
1867 len = strlen(ctl_line);
1869 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1870 ctlstrip = g_strndup(ctl_line, len - 1);
1871 else ctlstrip = g_strdup(ctl_line);
1873 tokens = g_strsplit(ctlstrip, " ", 2);
1874 parse_command(tokens[0], tokens[1], result);
1881 build_stream_name(int type, const gchar* dir) {
1882 char *xwin_str = NULL;
1883 State *s = &uzbl.state;
1886 xwin_str = itos((int)uzbl.xwin);
1888 str = g_strdup_printf
1889 ("%s/uzbl_fifo_%s", dir,
1890 s->instance_name ? s->instance_name : xwin_str);
1891 } else if (type == SOCKET) {
1892 str = g_strdup_printf
1893 ("%s/uzbl_socket_%s", dir,
1894 s->instance_name ? s->instance_name : xwin_str );
1901 control_fifo(GIOChannel *gio, GIOCondition condition) {
1902 if (uzbl.state.verbose)
1903 printf("triggered\n");
1908 if (condition & G_IO_HUP)
1909 g_error ("Fifo: Read end of pipe died!\n");
1912 g_error ("Fifo: GIOChannel broke\n");
1914 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1915 if (ret == G_IO_STATUS_ERROR) {
1916 g_error ("Fifo: Error reading: %s\n", err->message);
1920 parse_cmd_line(ctl_line, NULL);
1927 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1928 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1929 if (unlink(uzbl.comm.fifo_path) == -1)
1930 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1931 g_free(uzbl.comm.fifo_path);
1932 uzbl.comm.fifo_path = NULL;
1935 if (*dir == ' ') { /* space unsets the variable */
1940 GIOChannel *chan = NULL;
1941 GError *error = NULL;
1942 gchar *path = build_stream_name(FIFO, dir);
1944 if (!file_exists(path)) {
1945 if (mkfifo (path, 0666) == 0) {
1946 // 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.
1947 chan = g_io_channel_new_file(path, "r+", &error);
1949 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1950 if (uzbl.state.verbose)
1951 printf ("init_fifo: created successfully as %s\n", path);
1952 uzbl.comm.fifo_path = path;
1954 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1955 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1956 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1957 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1959 /* if we got this far, there was an error; cleanup */
1960 if (error) g_error_free (error);
1967 control_stdin(GIOChannel *gio, GIOCondition condition) {
1969 gchar *ctl_line = NULL;
1972 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1973 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1976 parse_cmd_line(ctl_line, NULL);
1984 GIOChannel *chan = NULL;
1985 GError *error = NULL;
1987 chan = g_io_channel_unix_new(fileno(stdin));
1989 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1990 g_error ("Stdin: could not add watch\n");
1992 if (uzbl.state.verbose)
1993 printf ("Stdin: watch added successfully\n");
1996 g_error ("Stdin: Error while opening: %s\n", error->message);
1998 if (error) g_error_free (error);
2002 control_socket(GIOChannel *chan) {
2003 struct sockaddr_un remote;
2004 unsigned int t = sizeof(remote);
2006 GIOChannel *clientchan;
2008 clientsock = accept (g_io_channel_unix_get_fd(chan),
2009 (struct sockaddr *) &remote, &t);
2011 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2012 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2013 (GIOFunc) control_client_socket, clientchan);
2020 control_client_socket(GIOChannel *clientchan) {
2022 GString *result = g_string_new("");
2023 GError *error = NULL;
2027 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2028 if (ret == G_IO_STATUS_ERROR) {
2029 g_warning ("Error reading: %s\n", error->message);
2030 g_io_channel_shutdown(clientchan, TRUE, &error);
2032 } else if (ret == G_IO_STATUS_EOF) {
2033 /* shutdown and remove channel watch from main loop */
2034 g_io_channel_shutdown(clientchan, TRUE, &error);
2039 parse_cmd_line (ctl_line, result);
2040 g_string_append_c(result, '\n');
2041 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2043 if (ret == G_IO_STATUS_ERROR) {
2044 g_warning ("Error writing: %s", error->message);
2046 g_io_channel_flush(clientchan, &error);
2049 if (error) g_error_free (error);
2050 g_string_free(result, TRUE);
2056 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2057 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2058 if (unlink(uzbl.comm.socket_path) == -1)
2059 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2060 g_free(uzbl.comm.socket_path);
2061 uzbl.comm.socket_path = NULL;
2069 GIOChannel *chan = NULL;
2071 struct sockaddr_un local;
2072 gchar *path = build_stream_name(SOCKET, dir);
2074 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2076 local.sun_family = AF_UNIX;
2077 strcpy (local.sun_path, path);
2078 unlink (local.sun_path);
2080 len = strlen (local.sun_path) + sizeof (local.sun_family);
2081 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2082 if (uzbl.state.verbose)
2083 printf ("init_socket: opened in %s\n", path);
2086 if( (chan = g_io_channel_unix_new(sock)) ) {
2087 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2088 uzbl.comm.socket_path = path;
2091 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2093 /* if we got this far, there was an error; cleanup */
2100 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2101 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2103 // this function may be called very early when the templates are not set (yet), hence the checks
2105 update_title (void) {
2106 Behaviour *b = &uzbl.behave;
2109 if (b->show_status) {
2110 if (b->title_format_short) {
2111 parsed = expand_template(b->title_format_short, FALSE);
2112 if (uzbl.gui.main_window)
2113 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2116 if (b->status_format) {
2117 parsed = expand_template(b->status_format, TRUE);
2118 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2121 if (b->status_background) {
2123 gdk_color_parse (b->status_background, &color);
2124 //labels and hboxes do not draw their own background. applying this on the vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2125 if (uzbl.gui.main_window)
2126 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2127 else if (uzbl.gui.plug)
2128 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2131 if (b->title_format_long) {
2132 parsed = expand_template(b->title_format_long, FALSE);
2133 if (uzbl.gui.main_window)
2134 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2141 key_press_cb (GtkWidget* window, GdkEventKey* event)
2143 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2147 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2148 || 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)
2151 /* turn off insert mode (if always_insert_mode is not used) */
2152 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2153 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2158 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2161 if (event->keyval == GDK_Escape) {
2162 g_string_truncate(uzbl.state.keycmd, 0);
2164 dehilight(uzbl.gui.web_view, NULL, NULL);
2168 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2169 if (event->keyval == GDK_Insert) {
2171 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2172 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2174 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2177 g_string_append (uzbl.state.keycmd, str);
2184 if (event->keyval == GDK_BackSpace)
2185 keycmd_bs(NULL, NULL, NULL);
2187 gboolean key_ret = FALSE;
2188 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2190 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2192 run_keycmd(key_ret);
2194 if (key_ret) return (!uzbl.behave.insert_mode);
2199 run_keycmd(const gboolean key_ret) {
2200 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2202 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2203 g_string_truncate(uzbl.state.keycmd, 0);
2204 parse_command(act->name, act->param, NULL);
2208 /* try if it's an incremental keycmd or one that takes args, and run it */
2209 GString* short_keys = g_string_new ("");
2210 GString* short_keys_inc = g_string_new ("");
2212 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2213 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2214 g_string_assign(short_keys_inc, short_keys->str);
2215 g_string_append_c(short_keys, '_');
2216 g_string_append_c(short_keys_inc, '*');
2218 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2219 /* run normal cmds only if return was pressed */
2220 exec_paramcmd(act, i);
2221 g_string_truncate(uzbl.state.keycmd, 0);
2223 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2224 if (key_ret) /* just quit the incremental command on return */
2225 g_string_truncate(uzbl.state.keycmd, 0);
2226 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2230 g_string_truncate(short_keys, short_keys->len - 1);
2232 g_string_free (short_keys, TRUE);
2233 g_string_free (short_keys_inc, TRUE);
2237 exec_paramcmd(const Action *act, const guint i) {
2238 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2239 GString *actionname = g_string_new ("");
2240 GString *actionparam = g_string_new ("");
2241 g_string_erase (parampart, 0, i+1);
2243 g_string_printf (actionname, act->name, parampart->str);
2245 g_string_printf (actionparam, act->param, parampart->str);
2246 parse_command(actionname->str, actionparam->str, NULL);
2247 g_string_free(actionname, TRUE);
2248 g_string_free(actionparam, TRUE);
2249 g_string_free(parampart, TRUE);
2257 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2258 //main_window_ref = g_object_ref(scrolled_window);
2259 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
2261 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2262 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2264 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2265 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2268 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2269 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2270 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2271 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2272 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2273 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2274 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2276 return scrolled_window;
2283 g->mainbar = gtk_hbox_new (FALSE, 0);
2285 /* keep a reference to the bar so we can re-pack it at runtime*/
2286 //sbar_ref = g_object_ref(g->mainbar);
2288 g->mainbar_label = gtk_label_new ("");
2289 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2290 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2291 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2292 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2293 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2294 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2299 GtkWidget* create_window () {
2300 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2301 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2302 gtk_widget_set_name (window, "Uzbl browser");
2303 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2304 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2310 GtkPlug* create_plug () {
2311 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2312 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2313 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2320 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2322 If actname is one that calls an external command, this function will inject
2323 newargs in front of the user-provided args in that command line. They will
2324 come become after the body of the script (in sh) or after the name of
2325 the command to execute (in spawn).
2326 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2327 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2329 The return value consist of two strings: the action (sh, ...) and its args.
2331 If act is not one that calls an external command, then the given action merely
2334 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2335 gchar *actdup = g_strdup(actname);
2336 g_array_append_val(rets, actdup);
2338 if ((g_strcmp0(actname, "spawn") == 0) ||
2339 (g_strcmp0(actname, "sh") == 0) ||
2340 (g_strcmp0(actname, "sync_spawn") == 0) ||
2341 (g_strcmp0(actname, "sync_sh") == 0)) {
2343 GString *a = g_string_new("");
2344 gchar **spawnparts = split_quoted(origargs, FALSE);
2345 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2346 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2348 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2349 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2351 g_array_append_val(rets, a->str);
2352 g_string_free(a, FALSE);
2353 g_strfreev(spawnparts);
2355 gchar *origdup = g_strdup(origargs);
2356 g_array_append_val(rets, origdup);
2358 return (gchar**)g_array_free(rets, FALSE);
2362 run_handler (const gchar *act, const gchar *args) {
2363 /* Consider this code a temporary hack to make the handlers usable.
2364 In practice, all this splicing, injection, and reconstruction is
2365 inefficient, annoying and hard to manage. Potential pitfalls arise
2366 when the handler specific args 1) are not quoted (the handler
2367 callbacks should take care of this) 2) are quoted but interfere
2368 with the users' own quotation. A more ideal solution is
2369 to refactor parse_command so that it doesn't just take a string
2370 and execute it; rather than that, we should have a function which
2371 returns the argument vector parsed from the string. This vector
2372 could be modified (e.g. insert additional args into it) before
2373 passing it to the next function that actually executes it. Though
2374 it still isn't perfect for chain actions.. will reconsider & re-
2375 factor when I have the time. -duc */
2377 char **parts = g_strsplit(act, " ", 2);
2379 if (g_strcmp0(parts[0], "chain") == 0) {
2380 GString *newargs = g_string_new("");
2381 gchar **chainparts = split_quoted(parts[1], FALSE);
2383 /* for every argument in the chain, inject the handler args
2384 and make sure the new parts are wrapped in quotes */
2385 gchar **cp = chainparts;
2387 gchar *quotless = NULL;
2388 gchar **spliced_quotless = NULL; // sigh -_-;
2389 gchar **inpart = NULL;
2392 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2394 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2395 } else quotless = g_strdup(*cp);
2397 spliced_quotless = g_strsplit(quotless, " ", 2);
2398 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2399 g_strfreev(spliced_quotless);
2401 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2407 parse_command(parts[0], &(newargs->str[1]), NULL);
2408 g_string_free(newargs, TRUE);
2409 g_strfreev(chainparts);
2412 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2413 parse_command(inparts[0], inparts[1], NULL);
2421 add_binding (const gchar *key, const gchar *act) {
2422 char **parts = g_strsplit(act, " ", 2);
2429 if (uzbl.state.verbose)
2430 printf ("Binding %-10s : %s\n", key, act);
2431 action = new_action(parts[0], parts[1]);
2433 if (g_hash_table_remove (uzbl.bindings, key))
2434 g_warning ("Overwriting existing binding for \"%s\"", key);
2435 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2440 get_xdg_var (XDG_Var xdg) {
2441 const gchar* actual_value = getenv (xdg.environmental);
2442 const gchar* home = getenv ("HOME");
2443 gchar* return_value;
2445 if (! actual_value || strcmp (actual_value, "") == 0) {
2446 if (xdg.default_value) {
2447 return_value = str_replace ("~", home, xdg.default_value);
2449 return_value = NULL;
2452 return_value = str_replace("~", home, actual_value);
2455 return return_value;
2459 find_xdg_file (int xdg_type, char* filename) {
2460 /* xdg_type = 0 => config
2461 xdg_type = 1 => data
2462 xdg_type = 2 => cache*/
2464 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2465 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2468 gchar* temporary_string;
2472 if (! file_exists (temporary_file) && xdg_type != 2) {
2473 buf = get_xdg_var (XDG[3 + xdg_type]);
2474 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2477 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2478 g_free (temporary_file);
2479 temporary_file = g_strconcat (temporary_string, filename, NULL);
2483 //g_free (temporary_string); - segfaults.
2485 if (file_exists (temporary_file)) {
2486 return temporary_file;
2493 State *s = &uzbl.state;
2494 Network *n = &uzbl.net;
2496 for (i = 0; default_config[i].command != NULL; i++) {
2497 parse_cmd_line(default_config[i].command, NULL);
2500 if (g_strcmp0(s->config_file, "-") == 0) {
2501 s->config_file = NULL;
2505 else if (!s->config_file) {
2506 s->config_file = find_xdg_file (0, "/uzbl/config");
2509 if (s->config_file) {
2510 GArray* lines = read_file_by_line (s->config_file);
2514 while ((line = g_array_index(lines, gchar*, i))) {
2515 parse_cmd_line (line, NULL);
2519 g_array_free (lines, TRUE);
2521 if (uzbl.state.verbose)
2522 printf ("No configuration file loaded.\n");
2525 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2528 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2531 if (!uzbl.behave.cookie_handler)
2534 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2535 GString *s = g_string_new ("");
2536 SoupURI * soup_uri = soup_message_get_uri(msg);
2537 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2538 run_handler(uzbl.behave.cookie_handler, s->str);
2540 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2541 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2542 if ( p != NULL ) *p = '\0';
2543 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2545 if (uzbl.comm.sync_stdout)
2546 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2548 g_string_free(s, TRUE);
2552 save_cookies (SoupMessage *msg, gpointer user_data){
2556 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2557 cookie = soup_cookie_to_set_cookie_header(ck->data);
2558 SoupURI * soup_uri = soup_message_get_uri(msg);
2559 GString *s = g_string_new ("");
2560 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2561 run_handler(uzbl.behave.cookie_handler, s->str);
2563 g_string_free(s, TRUE);
2568 /* --- WEBINSPECTOR --- */
2570 hide_window_cb(GtkWidget *widget, gpointer data) {
2573 gtk_widget_hide(widget);
2576 static WebKitWebView*
2577 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2580 (void) web_inspector;
2581 GtkWidget* scrolled_window;
2582 GtkWidget* new_web_view;
2585 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2586 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2587 G_CALLBACK(hide_window_cb), NULL);
2589 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2590 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2591 gtk_widget_show(g->inspector_window);
2593 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2594 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2595 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2596 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2597 gtk_widget_show(scrolled_window);
2599 new_web_view = webkit_web_view_new();
2600 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2602 return WEBKIT_WEB_VIEW(new_web_view);
2606 inspector_show_window_cb (WebKitWebInspector* inspector){
2608 gtk_widget_show(uzbl.gui.inspector_window);
2612 /* TODO: Add variables and code to make use of these functions */
2614 inspector_close_window_cb (WebKitWebInspector* inspector){
2620 inspector_attach_window_cb (WebKitWebInspector* inspector){
2626 inspector_detach_window_cb (WebKitWebInspector* inspector){
2632 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2638 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2644 set_up_inspector() {
2646 WebKitWebSettings *settings = view_settings();
2647 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2649 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2650 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2651 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2652 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2653 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2654 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2655 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2657 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2661 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2663 uzbl_cmdprop *c = v;
2668 if(c->type == TYPE_STR)
2669 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2670 else if(c->type == TYPE_INT)
2671 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2675 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2679 printf("bind %s = %s %s\n", (char *)k ,
2680 (char *)a->name, a->param?(char *)a->param:"");
2685 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2686 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2691 main (int argc, char* argv[]) {
2692 gtk_init (&argc, &argv);
2693 if (!g_thread_supported ())
2694 g_thread_init (NULL);
2695 uzbl.state.executable_path = g_strdup(argv[0]);
2696 uzbl.state.selected_url = NULL;
2697 uzbl.state.searchtx = NULL;
2699 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2700 g_option_context_add_main_entries (context, entries, NULL);
2701 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2702 g_option_context_parse (context, &argc, &argv, NULL);
2703 g_option_context_free(context);
2705 if (uzbl.behave.print_version) {
2706 printf("Commit: %s\n", COMMIT);
2710 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2711 if (argc > 1 && !uzbl.state.uri)
2712 uri_override = g_strdup(argv[1]);
2713 gboolean verbose_override = uzbl.state.verbose;
2715 /* initialize hash table */
2716 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2718 uzbl.net.soup_session = webkit_get_default_session();
2719 uzbl.state.keycmd = g_string_new("");
2721 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2722 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2723 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2724 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2725 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2726 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2729 if(uname(&uzbl.state.unameinfo) == -1)
2730 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2732 uzbl.gui.sbar.progress_s = g_strdup("=");
2733 uzbl.gui.sbar.progress_u = g_strdup("·");
2734 uzbl.gui.sbar.progress_w = 10;
2736 /* HTML mode defaults*/
2737 uzbl.behave.html_buffer = g_string_new("");
2738 uzbl.behave.html_endmarker = g_strdup(".");
2739 uzbl.behave.html_timeout = 60;
2740 uzbl.behave.base_url = g_strdup("http://invalid");
2742 /* default mode indicators */
2743 uzbl.behave.insert_indicator = g_strdup("I");
2744 uzbl.behave.cmd_indicator = g_strdup("C");
2748 make_var_to_name_hash();
2750 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2752 uzbl.gui.scrolled_win = create_browser();
2755 /* initial packing */
2756 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2757 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2759 if (uzbl.state.socket_id) {
2760 uzbl.gui.plug = create_plug ();
2761 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2762 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2764 uzbl.gui.main_window = create_window ();
2765 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2766 gtk_widget_show_all (uzbl.gui.main_window);
2767 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2770 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2772 if (uzbl.state.verbose) {
2773 printf("Uzbl start location: %s\n", argv[0]);
2774 if (uzbl.state.socket_id)
2775 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2777 printf("window_id %i\n",(int) uzbl.xwin);
2778 printf("pid %i\n", getpid ());
2779 printf("name: %s\n", uzbl.state.instance_name);
2782 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2783 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2784 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2785 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2786 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2790 if (!uzbl.behave.show_status)
2791 gtk_widget_hide(uzbl.gui.mainbar);
2798 if (verbose_override > uzbl.state.verbose)
2799 uzbl.state.verbose = verbose_override;
2802 set_var_value("uri", uri_override);
2803 g_free(uri_override);
2804 } else if (uzbl.state.uri)
2805 cmd_load_uri(uzbl.gui.web_view, NULL);
2810 return EXIT_SUCCESS;
2813 /* vi: set et ts=4: */