1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
77 "Print the version and exit", NULL },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
83 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
84 the PTR() macro is kind of preventing this change at the moment. */
91 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
93 /* an abbreviation to help keep the table's width humane */
94 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* --------------------------------------------------------------------------------------- */
102 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
104 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
105 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
106 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
107 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
108 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
109 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
110 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
111 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
112 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
113 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
114 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
115 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
116 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
117 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
118 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
119 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
120 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
121 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
122 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
123 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
124 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
125 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
126 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
127 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
128 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
129 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
130 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
131 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
132 { "new_window", PTR(uzbl.behave.new_window, STR, 1, cmd_new_window)},
133 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
134 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
135 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
136 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
137 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
138 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
139 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
140 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
141 /* exported WebKitWebSettings properties */
142 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
143 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
144 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
145 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
146 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
147 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
148 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
149 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
150 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
151 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
152 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
153 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
154 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
155 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
156 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
157 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
159 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
160 }, *n2v_p = var_name_to_ptr;
166 { "SHIFT", GDK_SHIFT_MASK }, // shift
167 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
168 { "CONTROL", GDK_CONTROL_MASK }, // control
169 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
170 { "MOD2", GDK_MOD2_MASK }, // 5th mod
171 { "MOD3", GDK_MOD3_MASK }, // 6th mod
172 { "MOD4", GDK_MOD4_MASK }, // 7th mod
173 { "MOD5", GDK_MOD5_MASK }, // 8th mod
174 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
175 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
176 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
177 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
178 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
179 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
180 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
181 { "META", GDK_META_MASK }, // meta (since 2.10)
186 /* construct a hash from the var_name_to_ptr array for quick access */
188 make_var_to_name_hash() {
189 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
191 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
196 /* --- UTILITY FUNCTIONS --- */
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 g_string_append_printf(buf, "%d", (int)*c->ptr);
289 else if(c->type == TYPE_FLOAT) {
290 g_string_append_printf(buf, "%f", *(float *)c->ptr);
293 if(etype == EXP_SIMPLE_VAR)
298 else if(recurse != 1 &&
300 mycmd = expand(ret, 1);
301 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
305 g_printerr("error on running command: %s\n", err->message);
308 else if (*cmd_stdout) {
309 g_string_append(buf, cmd_stdout);
314 else if(recurse != 2 &&
316 mycmd = expand(ret, 2);
317 eval_js(uzbl.gui.web_view, mycmd, js_ret);
321 g_string_append(buf, js_ret->str);
322 g_string_free(js_ret, TRUE);
323 js_ret = g_string_new("");
330 g_string_append_c(buf, *s);
335 g_string_free(js_ret, TRUE);
336 return g_string_free(buf, FALSE);
343 snprintf(tmp, sizeof(tmp), "%i", val);
344 return g_strdup(tmp);
348 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
351 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
354 str_replace (const char* search, const char* replace, const char* string) {
358 buf = g_strsplit (string, search, -1);
359 ret = g_strjoinv (replace, buf);
360 g_strfreev(buf); // somebody said this segfaults
366 read_file_by_line (gchar *path) {
367 GIOChannel *chan = NULL;
368 gchar *readbuf = NULL;
370 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
373 chan = g_io_channel_new_file(path, "r", NULL);
376 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
377 const gchar* val = g_strdup (readbuf);
378 g_array_append_val (lines, val);
383 g_io_channel_unref (chan);
385 fprintf(stderr, "File '%s' not be read.\n", path);
392 parseenv (char* string) {
393 extern char** environ;
394 gchar* tmpstr = NULL;
398 while (environ[i] != NULL) {
399 gchar** env = g_strsplit (environ[i], "=", 2);
400 gchar* envname = g_strconcat ("$", env[0], NULL);
402 if (g_strrstr (string, envname) != NULL) {
403 tmpstr = g_strdup(string);
405 string = str_replace(envname, env[1], tmpstr);
410 g_strfreev (env); // somebody said this breaks uzbl
418 setup_signal(int signr, sigfunc *shandler) {
419 struct sigaction nh, oh;
421 nh.sa_handler = shandler;
422 sigemptyset(&nh.sa_mask);
425 if(sigaction(signr, &nh, &oh) < 0)
433 if (uzbl.behave.fifo_dir)
434 unlink (uzbl.comm.fifo_path);
435 if (uzbl.behave.socket_dir)
436 unlink (uzbl.comm.socket_path);
438 g_free(uzbl.state.executable_path);
439 g_string_free(uzbl.state.keycmd, TRUE);
440 g_hash_table_destroy(uzbl.bindings);
441 g_hash_table_destroy(uzbl.behave.commands);
442 g_scanner_destroy(uzbl.scan);
445 /* used for html_mode_timeout
446 * be sure to extend this function to use
447 * more timers if needed in other places
450 set_timeout(int seconds) {
452 memset(&t, 0, sizeof t);
454 t.it_value.tv_sec = seconds;
455 t.it_value.tv_usec = 0;
456 setitimer(ITIMER_REAL, &t, NULL);
459 /* --- SIGNAL HANDLER --- */
462 catch_sigterm(int s) {
468 catch_sigint(int s) {
478 set_var_value("mode", "0");
483 /* --- CALLBACKS --- */
486 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
489 (void) navigation_action;
490 (void) policy_decision;
492 const gchar* uri = webkit_network_request_get_uri (request);
493 if (uzbl.state.verbose)
494 printf("New window requested -> %s \n", uri);
495 webkit_web_policy_decision_use(policy_decision);
500 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
505 /* If we can display it, let's display it... */
506 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
507 webkit_web_policy_decision_use (policy_decision);
511 /* ...everything we can't displayed is downloaded */
512 webkit_web_policy_decision_download (policy_decision);
517 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
521 if (uzbl.state.selected_url != NULL) {
522 if (uzbl.state.verbose)
523 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
524 new_window_load_uri(uzbl.state.selected_url);
526 if (uzbl.state.verbose)
527 printf("New web view -> %s\n","Nothing to open, exiting");
533 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
536 if (uzbl.behave.download_handler) {
537 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
538 if (uzbl.state.verbose)
539 printf("Download -> %s\n",uri);
540 /* if urls not escaped, we may have to escape and quote uri before this call */
541 run_handler(uzbl.behave.download_handler, uri);
546 /* scroll a bar in a given direction */
548 scroll (GtkAdjustment* bar, GArray *argv) {
552 gdouble page_size = gtk_adjustment_get_page_size(bar);
553 gdouble value = gtk_adjustment_get_value(bar);
554 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
557 value += page_size * amount * 0.01;
561 max_value = gtk_adjustment_get_upper(bar) - page_size;
563 if (value > max_value)
564 value = max_value; /* don't scroll past the end of the page */
566 gtk_adjustment_set_value (bar, value);
570 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
571 (void) page; (void) argv; (void) result;
572 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
576 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
577 (void) page; (void) argv; (void) result;
578 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
579 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
583 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
584 (void) page; (void) result;
585 scroll(uzbl.gui.bar_v, argv);
589 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
590 (void) page; (void) result;
591 scroll(uzbl.gui.bar_h, argv);
596 if (!uzbl.behave.show_status) {
597 gtk_widget_hide(uzbl.gui.mainbar);
599 gtk_widget_show(uzbl.gui.mainbar);
605 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
610 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
614 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
619 if (uzbl.behave.show_status) {
620 gtk_widget_hide(uzbl.gui.mainbar);
622 gtk_widget_show(uzbl.gui.mainbar);
624 uzbl.behave.show_status = !uzbl.behave.show_status;
629 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
633 //Set selected_url state variable
634 g_free(uzbl.state.selected_url);
635 uzbl.state.selected_url = NULL;
637 uzbl.state.selected_url = g_strdup(link);
643 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
646 const gchar *title = webkit_web_view_get_title(web_view);
647 if (uzbl.gui.main_title)
648 g_free (uzbl.gui.main_title);
649 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
654 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
657 uzbl.gui.sbar.load_progress = progress;
662 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
666 if (uzbl.behave.load_finish_handler)
667 run_handler(uzbl.behave.load_finish_handler, "");
671 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
675 uzbl.gui.sbar.load_progress = 0;
676 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
677 if (uzbl.behave.load_start_handler)
678 run_handler(uzbl.behave.load_start_handler, "");
682 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
685 g_free (uzbl.state.uri);
686 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
687 uzbl.state.uri = g_string_free (newuri, FALSE);
688 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
689 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
692 if (uzbl.behave.load_commit_handler)
693 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
697 destroy_cb (GtkWidget* widget, gpointer data) {
705 if (uzbl.behave.history_handler) {
707 struct tm * timeinfo;
710 timeinfo = localtime ( &rawtime );
711 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
712 run_handler(uzbl.behave.history_handler, date);
717 /* VIEW funcs (little webkit wrappers) */
718 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
720 VIEWFUNC(reload_bypass_cache)
721 VIEWFUNC(stop_loading)
728 /* -- command to callback/function map for things we cannot attach to any signals */
729 struct {char *key; CommandInfo value;} cmdlist[] =
730 { /* key function no_split */
731 { "back", {view_go_back, 0} },
732 { "forward", {view_go_forward, 0} },
733 { "scroll_vert", {scroll_vert, 0} },
734 { "scroll_horz", {scroll_horz, 0} },
735 { "scroll_begin", {scroll_begin, 0} },
736 { "scroll_end", {scroll_end, 0} },
737 { "reload", {view_reload, 0}, },
738 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
739 { "stop", {view_stop_loading, 0}, },
740 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
741 { "zoom_out", {view_zoom_out, 0}, },
742 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
743 { "uri", {load_uri, TRUE} },
744 { "js", {run_js, TRUE} },
745 { "script", {run_external_js, 0} },
746 { "toggle_status", {toggle_status_cb, 0} },
747 { "spawn", {spawn, 0} },
748 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
749 { "sh", {spawn_sh, 0} },
750 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
751 { "exit", {close_uzbl, 0} },
752 { "search", {search_forward_text, TRUE} },
753 { "search_reverse", {search_reverse_text, TRUE} },
754 { "dehilight", {dehilight, 0} },
755 { "toggle_insert_mode", {toggle_insert_mode, 0} },
756 { "set", {set_var, TRUE} },
757 //{ "get", {get_var, TRUE} },
758 { "bind", {act_bind, TRUE} },
759 { "dump_config", {act_dump_config, 0} },
760 { "keycmd", {keycmd, TRUE} },
761 { "keycmd_nl", {keycmd_nl, TRUE} },
762 { "keycmd_bs", {keycmd_bs, 0} },
763 { "chain", {chain, 0} },
764 { "print", {print, TRUE} }
771 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
773 for (i = 0; i < LENGTH(cmdlist); i++)
774 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
777 /* -- CORE FUNCTIONS -- */
780 free_action(gpointer act) {
781 Action *action = (Action*)act;
782 g_free(action->name);
784 g_free(action->param);
789 new_action(const gchar *name, const gchar *param) {
790 Action *action = g_new(Action, 1);
792 action->name = g_strdup(name);
794 action->param = g_strdup(param);
796 action->param = NULL;
802 file_exists (const char * filename) {
803 return (access(filename, F_OK) == 0);
807 set_var(WebKitWebView *page, GArray *argv, GString *result) {
808 (void) page; (void) result;
809 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
810 if (split[0] != NULL) {
811 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
812 set_var_value(g_strstrip(split[0]), value);
819 print(WebKitWebView *page, GArray *argv, GString *result) {
820 (void) page; (void) result;
823 buf = expand(argv_idx(argv, 0), 0);
824 g_string_assign(result, buf);
829 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
830 (void) page; (void) result;
831 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
832 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
833 add_binding(g_strstrip(split[0]), value);
845 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
846 (void) page; (void) result;
848 if (argv_idx(argv, 0)) {
849 if (strcmp (argv_idx(argv, 0), "0") == 0) {
850 uzbl.behave.insert_mode = FALSE;
852 uzbl.behave.insert_mode = TRUE;
855 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
862 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
865 if (argv_idx(argv, 0)) {
866 GString* newuri = g_string_new (argv_idx(argv, 0));
867 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
868 run_js(web_view, argv, NULL);
871 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
872 g_string_prepend (newuri, "http://");
873 /* if we do handle cookies, ask our handler for them */
874 webkit_web_view_load_uri (web_view, newuri->str);
875 g_string_free (newuri, TRUE);
882 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
883 size_t argumentCount, const JSValueRef arguments[],
884 JSValueRef* exception) {
889 JSStringRef js_result_string;
890 GString *result = g_string_new("");
892 if (argumentCount >= 1) {
893 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
894 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
895 char ctl_line[arg_size];
896 JSStringGetUTF8CString(arg, ctl_line, arg_size);
898 parse_cmd_line(ctl_line, result);
900 JSStringRelease(arg);
902 js_result_string = JSStringCreateWithUTF8CString(result->str);
904 g_string_free(result, TRUE);
906 return JSValueMakeString(ctx, js_result_string);
909 JSStaticFunction js_static_functions[] = {
910 {"run", js_run_command, kJSPropertyAttributeNone},
915 /* This function creates the class and its definition, only once */
916 if (!uzbl.js.initialized) {
917 /* it would be pretty cool to make this dynamic */
918 uzbl.js.classdef = kJSClassDefinitionEmpty;
919 uzbl.js.classdef.staticFunctions = js_static_functions;
921 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
927 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
928 WebKitWebFrame *frame;
929 JSGlobalContextRef context;
930 JSObjectRef globalobject;
931 JSStringRef var_name;
933 JSStringRef js_script;
934 JSValueRef js_result;
935 JSStringRef js_result_string;
936 size_t js_result_size;
940 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
941 context = webkit_web_frame_get_global_context(frame);
942 globalobject = JSContextGetGlobalObject(context);
944 /* uzbl javascript namespace */
945 var_name = JSStringCreateWithUTF8CString("Uzbl");
946 JSObjectSetProperty(context, globalobject, var_name,
947 JSObjectMake(context, uzbl.js.classref, NULL),
948 kJSClassAttributeNone, NULL);
950 /* evaluate the script and get return value*/
951 js_script = JSStringCreateWithUTF8CString(script);
952 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
953 if (js_result && !JSValueIsUndefined(context, js_result)) {
954 js_result_string = JSValueToStringCopy(context, js_result, NULL);
955 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
957 if (js_result_size) {
958 char js_result_utf8[js_result_size];
959 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
960 g_string_assign(result, js_result_utf8);
963 JSStringRelease(js_result_string);
967 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
969 JSStringRelease(var_name);
970 JSStringRelease(js_script);
974 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
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 if (uzbl.behave.new_window) {
1053 GString *s = g_string_new ("");
1054 g_string_printf(s, "'%s'", uri);
1055 run_handler(uzbl.behave.new_window, s->str);
1058 GString* to_execute = g_string_new ("");
1059 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1061 for (i = 0; entries[i].long_name != NULL; i++) {
1062 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1063 gchar** str = (gchar**)entries[i].arg_data;
1065 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1069 if (uzbl.state.verbose)
1070 printf("\n%s\n", to_execute->str);
1071 g_spawn_command_line_async (to_execute->str, NULL);
1072 g_string_free (to_execute, TRUE);
1076 chain (WebKitWebView *page, GArray *argv, GString *result) {
1077 (void) page; (void) result;
1079 gchar **parts = NULL;
1081 while ((a = argv_idx(argv, i++))) {
1082 parts = g_strsplit (a, " ", 2);
1084 parse_command(parts[0], parts[1], result);
1090 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1094 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1100 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1104 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1110 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1115 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1117 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1122 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1129 /* --Statusbar functions-- */
1131 build_progressbar_ascii(int percent) {
1132 int width=uzbl.gui.sbar.progress_w;
1135 GString *bar = g_string_new("");
1137 l = (double)percent*((double)width/100.);
1138 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1140 for(i=0; i<(int)l; i++)
1141 g_string_append(bar, uzbl.gui.sbar.progress_s);
1144 g_string_append(bar, uzbl.gui.sbar.progress_u);
1146 return g_string_free(bar, FALSE);
1151 const GScannerConfig scan_config = {
1154 ) /* cset_skip_characters */,
1159 ) /* cset_identifier_first */,
1166 ) /* cset_identifier_nth */,
1167 ( "" ) /* cpair_comment_single */,
1169 TRUE /* case_sensitive */,
1171 FALSE /* skip_comment_multi */,
1172 FALSE /* skip_comment_single */,
1173 FALSE /* scan_comment_multi */,
1174 TRUE /* scan_identifier */,
1175 TRUE /* scan_identifier_1char */,
1176 FALSE /* scan_identifier_NULL */,
1177 TRUE /* scan_symbols */,
1178 FALSE /* scan_binary */,
1179 FALSE /* scan_octal */,
1180 FALSE /* scan_float */,
1181 FALSE /* scan_hex */,
1182 FALSE /* scan_hex_dollar */,
1183 FALSE /* scan_string_sq */,
1184 FALSE /* scan_string_dq */,
1185 TRUE /* numbers_2_int */,
1186 FALSE /* int_2_float */,
1187 FALSE /* identifier_2_string */,
1188 FALSE /* char_2_token */,
1189 FALSE /* symbol_2_token */,
1190 TRUE /* scope_0_fallback */,
1195 uzbl.scan = g_scanner_new(&scan_config);
1196 while(symp->symbol_name) {
1197 g_scanner_scope_add_symbol(uzbl.scan, 0,
1199 GINT_TO_POINTER(symp->symbol_token));
1205 expand_template(const char *template, gboolean escape_markup) {
1206 if(!template) return NULL;
1208 GTokenType token = G_TOKEN_NONE;
1209 GString *ret = g_string_new("");
1213 g_scanner_input_text(uzbl.scan, template, strlen(template));
1214 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1215 token = g_scanner_get_next_token(uzbl.scan);
1217 if(token == G_TOKEN_SYMBOL) {
1218 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1222 buf = uzbl.state.uri ?
1223 g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1224 g_string_append(ret, buf);
1228 g_string_append(ret, uzbl.state.uri ?
1229 uzbl.state.uri : g_strdup(""));
1232 buf = itos(uzbl.gui.sbar.load_progress);
1233 g_string_append(ret, buf);
1236 case SYM_LOADPRGSBAR:
1237 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1238 g_string_append(ret, buf);
1243 buf = uzbl.gui.main_title ?
1244 g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1245 g_string_append(ret, buf);
1249 g_string_append(ret, uzbl.gui.main_title ?
1250 uzbl.gui.main_title : "");
1252 case SYM_SELECTED_URI:
1254 buf = uzbl.state.selected_url ?
1255 g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1256 g_string_append(ret, buf);
1260 g_string_append(ret, uzbl.state.selected_url ?
1261 uzbl.state.selected_url : "");
1264 buf = itos(uzbl.xwin);
1265 g_string_append(ret,
1266 uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1271 buf = uzbl.state.keycmd->str ?
1272 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1273 g_string_append(ret, buf);
1277 g_string_append(ret, uzbl.state.keycmd->str ?
1278 uzbl.state.keycmd->str : "");
1281 g_string_append(ret,
1282 uzbl.behave.insert_mode ?
1283 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1286 g_string_append(ret,
1287 uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1289 /* useragent syms */
1291 buf = itos(WEBKIT_MAJOR_VERSION);
1292 g_string_append(ret, buf);
1296 buf = itos(WEBKIT_MINOR_VERSION);
1297 g_string_append(ret, buf);
1301 buf = itos(WEBKIT_MICRO_VERSION);
1302 g_string_append(ret, buf);
1306 g_string_append(ret, uzbl.state.unameinfo.sysname);
1309 g_string_append(ret, uzbl.state.unameinfo.nodename);
1312 g_string_append(ret, uzbl.state.unameinfo.release);
1315 g_string_append(ret, uzbl.state.unameinfo.version);
1318 g_string_append(ret, uzbl.state.unameinfo.machine);
1321 g_string_append(ret, ARCH);
1324 case SYM_DOMAINNAME:
1325 g_string_append(ret, uzbl.state.unameinfo.domainname);
1329 g_string_append(ret, COMMIT);
1335 else if(token == G_TOKEN_INT) {
1336 g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1338 else if(token == G_TOKEN_IDENTIFIER) {
1339 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1341 else if(token == G_TOKEN_CHAR) {
1342 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1344 else if(token == G_TOKEN_ERROR) {
1345 g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.",
1347 g_scanner_cur_line(uzbl.scan),
1348 g_scanner_cur_position(uzbl.scan));
1352 return g_string_free(ret, FALSE);
1354 /* --End Statusbar functions-- */
1357 sharg_append(GArray *a, const gchar *str) {
1358 const gchar *s = (str ? str : "");
1359 g_array_append_val(a, s);
1362 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1364 run_command (const gchar *command, const guint npre, const gchar **args,
1365 const gboolean sync, char **output_stdout) {
1366 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1369 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1370 gchar *pid = itos(getpid());
1371 gchar *xwin = itos(uzbl.xwin);
1373 sharg_append(a, command);
1374 for (i = 0; i < npre; i++) /* add n args before the default vars */
1375 sharg_append(a, args[i]);
1376 sharg_append(a, uzbl.state.config_file);
1377 sharg_append(a, pid);
1378 sharg_append(a, xwin);
1379 sharg_append(a, uzbl.comm.fifo_path);
1380 sharg_append(a, uzbl.comm.socket_path);
1381 sharg_append(a, uzbl.state.uri);
1382 sharg_append(a, uzbl.gui.main_title);
1384 for (i = npre; i < g_strv_length((gchar**)args); i++)
1385 sharg_append(a, args[i]);
1389 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1391 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1392 NULL, NULL, output_stdout, NULL, NULL, &err);
1393 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1394 NULL, NULL, NULL, &err);
1396 if (uzbl.state.verbose) {
1397 GString *s = g_string_new("spawned:");
1398 for (i = 0; i < (a->len); i++) {
1399 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1400 g_string_append_printf(s, " %s", qarg);
1403 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1404 printf("%s\n", s->str);
1405 g_string_free(s, TRUE);
1407 printf("Stdout: %s\n", *output_stdout);
1411 g_printerr("error on run_command: %s\n", err->message);
1416 g_array_free (a, TRUE);
1421 split_quoted(const gchar* src, const gboolean unquote) {
1422 /* split on unquoted space, return array of strings;
1423 remove a layer of quotes and backslashes if unquote */
1424 if (!src) return NULL;
1426 gboolean dq = FALSE;
1427 gboolean sq = FALSE;
1428 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1429 GString *s = g_string_new ("");
1433 for (p = src; *p != '\0'; p++) {
1434 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1435 else if (*p == '\\') { g_string_append_c(s, *p++);
1436 g_string_append_c(s, *p); }
1437 else if ((*p == '"') && unquote && !sq) dq = !dq;
1438 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1440 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1441 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1443 else if ((*p == ' ') && !dq && !sq) {
1444 dup = g_strdup(s->str);
1445 g_array_append_val(a, dup);
1446 g_string_truncate(s, 0);
1447 } else g_string_append_c(s, *p);
1449 dup = g_strdup(s->str);
1450 g_array_append_val(a, dup);
1451 ret = (gchar**)a->data;
1452 g_array_free (a, FALSE);
1453 g_string_free (s, TRUE);
1458 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1459 (void)web_view; (void)result;
1460 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1461 if (argv_idx(argv, 0))
1462 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1466 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1467 (void)web_view; (void)result;
1469 if (argv_idx(argv, 0))
1470 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1471 TRUE, &uzbl.comm.sync_stdout);
1475 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1476 (void)web_view; (void)result;
1477 if (!uzbl.behave.shell_cmd) {
1478 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1483 gchar *spacer = g_strdup("");
1484 g_array_insert_val(argv, 1, spacer);
1485 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1487 for (i = 1; i < g_strv_length(cmd); i++)
1488 g_array_prepend_val(argv, cmd[i]);
1490 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1496 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1497 (void)web_view; (void)result;
1498 if (!uzbl.behave.shell_cmd) {
1499 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1504 gchar *spacer = g_strdup("");
1505 g_array_insert_val(argv, 1, spacer);
1506 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1508 for (i = 1; i < g_strv_length(cmd); i++)
1509 g_array_prepend_val(argv, cmd[i]);
1511 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1512 TRUE, &uzbl.comm.sync_stdout);
1518 parse_command(const char *cmd, const char *param, GString *result) {
1521 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1523 gchar **par = split_quoted(param, TRUE);
1524 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1526 if (c->no_split) { /* don't split */
1527 sharg_append(a, param);
1529 for (i = 0; i < g_strv_length(par); i++)
1530 sharg_append(a, par[i]);
1533 if (result == NULL) {
1534 GString *result_print = g_string_new("");
1536 c->function(uzbl.gui.web_view, a, result_print);
1537 if (result_print->len)
1538 printf("%*s\n", result_print->len, result_print->str);
1540 g_string_free(result_print, TRUE);
1542 c->function(uzbl.gui.web_view, a, result);
1545 g_array_free (a, TRUE);
1548 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1555 if(*uzbl.net.proxy_url == ' '
1556 || uzbl.net.proxy_url == NULL) {
1557 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1558 (GType) SOUP_SESSION_PROXY_URI);
1561 suri = soup_uri_new(uzbl.net.proxy_url);
1562 g_object_set(G_OBJECT(uzbl.net.soup_session),
1563 SOUP_SESSION_PROXY_URI,
1565 soup_uri_free(suri);
1572 if(file_exists(uzbl.gui.icon)) {
1573 if (uzbl.gui.main_window)
1574 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1576 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1582 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1583 g_array_append_val (a, uzbl.state.uri);
1584 load_uri(uzbl.gui.web_view, a, NULL);
1585 g_array_free (a, TRUE);
1589 cmd_always_insert_mode() {
1590 uzbl.behave.insert_mode =
1591 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1597 g_object_set(G_OBJECT(uzbl.net.soup_session),
1598 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1602 cmd_max_conns_host() {
1603 g_object_set(G_OBJECT(uzbl.net.soup_session),
1604 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1609 soup_session_remove_feature
1610 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1611 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1612 /*g_free(uzbl.net.soup_logger);*/
1614 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1615 soup_session_add_feature(uzbl.net.soup_session,
1616 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1621 return webkit_web_view_get_settings(uzbl.gui.web_view);
1626 WebKitWebSettings *ws = view_settings();
1627 if (uzbl.behave.font_size > 0) {
1628 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1631 if (uzbl.behave.monospace_size > 0) {
1632 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1633 uzbl.behave.monospace_size, NULL);
1635 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1636 uzbl.behave.font_size, NULL);
1642 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1646 cmd_disable_plugins() {
1647 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1648 !uzbl.behave.disable_plugins, NULL);
1652 cmd_disable_scripts() {
1653 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1654 !uzbl.behave.disable_scripts, NULL);
1658 cmd_minimum_font_size() {
1659 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1660 uzbl.behave.minimum_font_size, NULL);
1663 cmd_autoload_img() {
1664 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1665 uzbl.behave.autoload_img, NULL);
1670 cmd_autoshrink_img() {
1671 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1672 uzbl.behave.autoshrink_img, NULL);
1677 cmd_enable_spellcheck() {
1678 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1679 uzbl.behave.enable_spellcheck, NULL);
1683 cmd_enable_private() {
1684 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1685 uzbl.behave.enable_private, NULL);
1690 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1691 uzbl.behave.print_bg, NULL);
1696 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1697 uzbl.behave.style_uri, NULL);
1701 cmd_resizable_txt() {
1702 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1703 uzbl.behave.resizable_txt, NULL);
1707 cmd_default_encoding() {
1708 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1709 uzbl.behave.default_encoding, NULL);
1713 cmd_enforce_96dpi() {
1714 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1715 uzbl.behave.enforce_96dpi, NULL);
1719 cmd_caret_browsing() {
1720 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1721 uzbl.behave.caret_browsing, NULL);
1725 cmd_cookie_handler() {
1726 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1727 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1728 if ((g_strcmp0(split[0], "sh") == 0) ||
1729 (g_strcmp0(split[0], "spawn") == 0)) {
1730 g_free (uzbl.behave.cookie_handler);
1731 uzbl.behave.cookie_handler =
1732 g_strdup_printf("sync_%s %s", split[0], split[1]);
1739 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1740 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1741 if ((g_strcmp0(split[0], "sh") == 0) ||
1742 (g_strcmp0(split[0], "spawn") == 0)) {
1743 g_free (uzbl.behave.new_window);
1744 uzbl.behave.new_window =
1745 g_strdup_printf("%s %s", split[0], split[1]);
1752 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1757 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1762 if(uzbl.behave.inject_html) {
1763 webkit_web_view_load_html_string (uzbl.gui.web_view,
1764 uzbl.behave.inject_html, NULL);
1773 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1774 uzbl.behave.modmask = 0;
1776 if(uzbl.behave.modkey)
1777 g_free(uzbl.behave.modkey);
1778 uzbl.behave.modkey = buf;
1780 for (i = 0; modkeys[i].key != NULL; i++) {
1781 if (g_strrstr(buf, modkeys[i].key))
1782 uzbl.behave.modmask |= modkeys[i].mask;
1788 if (*uzbl.net.useragent == ' ') {
1789 g_free (uzbl.net.useragent);
1790 uzbl.net.useragent = NULL;
1792 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1794 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1795 g_free(uzbl.net.useragent);
1796 uzbl.net.useragent = ua;
1802 gtk_widget_ref(uzbl.gui.scrolled_win);
1803 gtk_widget_ref(uzbl.gui.mainbar);
1804 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1805 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1807 if(uzbl.behave.status_top) {
1808 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1809 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1812 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1813 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1815 gtk_widget_unref(uzbl.gui.scrolled_win);
1816 gtk_widget_unref(uzbl.gui.mainbar);
1817 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1822 set_var_value(gchar *name, gchar *val) {
1823 uzbl_cmdprop *c = NULL;
1827 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1828 /* check for the variable type */
1829 if (c->type == TYPE_STR) {
1830 buf = expand(val, 0);
1833 } else if(c->type == TYPE_INT) {
1834 int *ip = (int *)c->ptr;
1835 buf = expand(val, 0);
1836 *ip = (int)strtoul(buf, &endp, 10);
1838 } else if (c->type == TYPE_FLOAT) {
1839 float *fp = (float *)c->ptr;
1840 buf = expand(val, 0);
1841 *fp = strtod(buf, &endp);
1845 /* invoke a command specific function */
1846 if(c->func) c->func();
1853 Behaviour *b = &uzbl.behave;
1855 if(b->html_buffer->str) {
1856 webkit_web_view_load_html_string (uzbl.gui.web_view,
1857 b->html_buffer->str, b->base_url);
1858 g_string_free(b->html_buffer, TRUE);
1859 b->html_buffer = g_string_new("");
1863 enum {M_CMD, M_HTML};
1865 parse_cmd_line(const char *ctl_line, GString *result) {
1866 Behaviour *b = &uzbl.behave;
1869 if(b->mode == M_HTML) {
1870 len = strlen(b->html_endmarker);
1871 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1872 if(len == strlen(ctl_line)-1 &&
1873 !strncmp(b->html_endmarker, ctl_line, len)) {
1875 set_var_value("mode", "0");
1880 set_timeout(b->html_timeout);
1881 g_string_append(b->html_buffer, ctl_line);
1884 else if((ctl_line[0] == '#') /* Comments */
1885 || (ctl_line[0] == ' ')
1886 || (ctl_line[0] == '\n'))
1887 ; /* ignore these lines */
1888 else { /* parse a command */
1890 gchar **tokens = NULL;
1891 len = strlen(ctl_line);
1893 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1894 ctlstrip = g_strndup(ctl_line, len - 1);
1895 else ctlstrip = g_strdup(ctl_line);
1897 tokens = g_strsplit(ctlstrip, " ", 2);
1898 parse_command(tokens[0], tokens[1], result);
1905 build_stream_name(int type, const gchar* dir) {
1906 char *xwin_str = NULL;
1907 State *s = &uzbl.state;
1910 xwin_str = itos((int)uzbl.xwin);
1912 str = g_strdup_printf
1913 ("%s/uzbl_fifo_%s", dir,
1914 s->instance_name ? s->instance_name : xwin_str);
1915 } else if (type == SOCKET) {
1916 str = g_strdup_printf
1917 ("%s/uzbl_socket_%s", dir,
1918 s->instance_name ? s->instance_name : xwin_str );
1925 control_fifo(GIOChannel *gio, GIOCondition condition) {
1926 if (uzbl.state.verbose)
1927 printf("triggered\n");
1932 if (condition & G_IO_HUP)
1933 g_error ("Fifo: Read end of pipe died!\n");
1936 g_error ("Fifo: GIOChannel broke\n");
1938 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1939 if (ret == G_IO_STATUS_ERROR) {
1940 g_error ("Fifo: Error reading: %s\n", err->message);
1944 parse_cmd_line(ctl_line, NULL);
1951 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1952 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1953 if (unlink(uzbl.comm.fifo_path) == -1)
1954 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1955 g_free(uzbl.comm.fifo_path);
1956 uzbl.comm.fifo_path = NULL;
1959 GIOChannel *chan = NULL;
1960 GError *error = NULL;
1961 gchar *path = build_stream_name(FIFO, dir);
1963 if (!file_exists(path)) {
1964 if (mkfifo (path, 0666) == 0) {
1965 // 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.
1966 chan = g_io_channel_new_file(path, "r+", &error);
1968 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1969 if (uzbl.state.verbose)
1970 printf ("init_fifo: created successfully as %s\n", path);
1971 uzbl.comm.fifo_path = path;
1973 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1974 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1975 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1976 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1978 /* if we got this far, there was an error; cleanup */
1979 if (error) g_error_free (error);
1986 control_stdin(GIOChannel *gio, GIOCondition condition) {
1988 gchar *ctl_line = NULL;
1991 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1992 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1995 parse_cmd_line(ctl_line, NULL);
2003 GIOChannel *chan = NULL;
2004 GError *error = NULL;
2006 chan = g_io_channel_unix_new(fileno(stdin));
2008 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2009 g_error ("Stdin: could not add watch\n");
2011 if (uzbl.state.verbose)
2012 printf ("Stdin: watch added successfully\n");
2015 g_error ("Stdin: Error while opening: %s\n", error->message);
2017 if (error) g_error_free (error);
2021 control_socket(GIOChannel *chan) {
2022 struct sockaddr_un remote;
2023 unsigned int t = sizeof(remote);
2025 GIOChannel *clientchan;
2027 clientsock = accept (g_io_channel_unix_get_fd(chan),
2028 (struct sockaddr *) &remote, &t);
2030 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2031 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2032 (GIOFunc) control_client_socket, clientchan);
2039 control_client_socket(GIOChannel *clientchan) {
2041 GString *result = g_string_new("");
2042 GError *error = NULL;
2046 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2047 if (ret == G_IO_STATUS_ERROR) {
2048 g_warning ("Error reading: %s\n", error->message);
2049 g_io_channel_shutdown(clientchan, TRUE, &error);
2051 } else if (ret == G_IO_STATUS_EOF) {
2052 /* shutdown and remove channel watch from main loop */
2053 g_io_channel_shutdown(clientchan, TRUE, &error);
2058 parse_cmd_line (ctl_line, result);
2059 g_string_append_c(result, '\n');
2060 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2062 if (ret == G_IO_STATUS_ERROR) {
2063 g_warning ("Error writing: %s", error->message);
2065 g_io_channel_flush(clientchan, &error);
2068 if (error) g_error_free (error);
2069 g_string_free(result, TRUE);
2075 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2076 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2077 if (unlink(uzbl.comm.socket_path) == -1)
2078 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2079 g_free(uzbl.comm.socket_path);
2080 uzbl.comm.socket_path = NULL;
2088 GIOChannel *chan = NULL;
2090 struct sockaddr_un local;
2091 gchar *path = build_stream_name(SOCKET, dir);
2093 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2095 local.sun_family = AF_UNIX;
2096 strcpy (local.sun_path, path);
2097 unlink (local.sun_path);
2099 len = strlen (local.sun_path) + sizeof (local.sun_family);
2100 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2101 if (uzbl.state.verbose)
2102 printf ("init_socket: opened in %s\n", path);
2105 if( (chan = g_io_channel_unix_new(sock)) ) {
2106 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2107 uzbl.comm.socket_path = path;
2110 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2112 /* if we got this far, there was an error; cleanup */
2119 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2120 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2122 // this function may be called very early when the templates are not set (yet), hence the checks
2124 update_title (void) {
2125 Behaviour *b = &uzbl.behave;
2128 if (b->show_status) {
2129 if (b->title_format_short) {
2130 parsed = expand_template(b->title_format_short, FALSE);
2131 if (uzbl.gui.main_window)
2132 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2135 if (b->status_format) {
2136 parsed = expand_template(b->status_format, TRUE);
2137 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2140 if (b->status_background) {
2142 gdk_color_parse (b->status_background, &color);
2143 //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2144 if (uzbl.gui.main_window)
2145 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2146 else if (uzbl.gui.plug)
2147 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2150 if (b->title_format_long) {
2151 parsed = expand_template(b->title_format_long, FALSE);
2152 if (uzbl.gui.main_window)
2153 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2160 key_press_cb (GtkWidget* window, GdkEventKey* event)
2162 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2166 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2167 || 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)
2170 /* turn off insert mode (if always_insert_mode is not used) */
2171 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2172 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2177 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2180 if (event->keyval == GDK_Escape) {
2181 g_string_truncate(uzbl.state.keycmd, 0);
2183 dehilight(uzbl.gui.web_view, NULL, NULL);
2187 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2188 if (event->keyval == GDK_Insert) {
2190 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2191 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2193 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2196 g_string_append (uzbl.state.keycmd, str);
2203 if (event->keyval == GDK_BackSpace)
2204 keycmd_bs(NULL, NULL, NULL);
2206 gboolean key_ret = FALSE;
2207 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2209 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2211 run_keycmd(key_ret);
2213 if (key_ret) return (!uzbl.behave.insert_mode);
2218 run_keycmd(const gboolean key_ret) {
2219 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2221 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2222 g_string_truncate(uzbl.state.keycmd, 0);
2223 parse_command(act->name, act->param, NULL);
2227 /* try if it's an incremental keycmd or one that takes args, and run it */
2228 GString* short_keys = g_string_new ("");
2229 GString* short_keys_inc = g_string_new ("");
2231 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2232 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2233 g_string_assign(short_keys_inc, short_keys->str);
2234 g_string_append_c(short_keys, '_');
2235 g_string_append_c(short_keys_inc, '*');
2237 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2238 /* run normal cmds only if return was pressed */
2239 exec_paramcmd(act, i);
2240 g_string_truncate(uzbl.state.keycmd, 0);
2242 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2243 if (key_ret) /* just quit the incremental command on return */
2244 g_string_truncate(uzbl.state.keycmd, 0);
2245 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2249 g_string_truncate(short_keys, short_keys->len - 1);
2251 g_string_free (short_keys, TRUE);
2252 g_string_free (short_keys_inc, TRUE);
2256 exec_paramcmd(const Action *act, const guint i) {
2257 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2258 GString *actionname = g_string_new ("");
2259 GString *actionparam = g_string_new ("");
2260 g_string_erase (parampart, 0, i+1);
2262 g_string_printf (actionname, act->name, parampart->str);
2264 g_string_printf (actionparam, act->param, parampart->str);
2265 parse_command(actionname->str, actionparam->str, NULL);
2266 g_string_free(actionname, TRUE);
2267 g_string_free(actionparam, TRUE);
2268 g_string_free(parampart, TRUE);
2276 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2277 //main_window_ref = g_object_ref(scrolled_window);
2278 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
2280 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2281 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2283 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2284 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2285 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2286 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2287 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2288 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2289 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2290 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2291 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2292 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2293 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2295 return scrolled_window;
2302 g->mainbar = gtk_hbox_new (FALSE, 0);
2304 /* keep a reference to the bar so we can re-pack it at runtime*/
2305 //sbar_ref = g_object_ref(g->mainbar);
2307 g->mainbar_label = gtk_label_new ("");
2308 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2309 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2310 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2311 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2312 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2313 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2319 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2320 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2321 gtk_widget_set_name (window, "Uzbl browser");
2322 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2323 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2330 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2331 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2332 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2339 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2341 If actname is one that calls an external command, this function will inject
2342 newargs in front of the user-provided args in that command line. They will
2343 come become after the body of the script (in sh) or after the name of
2344 the command to execute (in spawn).
2345 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2346 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2348 The return value consist of two strings: the action (sh, ...) and its args.
2350 If act is not one that calls an external command, then the given action merely
2353 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2354 /* Arrr! Here be memory leaks */
2355 gchar *actdup = g_strdup(actname);
2356 g_array_append_val(rets, actdup);
2358 if ((g_strcmp0(actname, "spawn") == 0) ||
2359 (g_strcmp0(actname, "sh") == 0) ||
2360 (g_strcmp0(actname, "sync_spawn") == 0) ||
2361 (g_strcmp0(actname, "sync_sh") == 0)) {
2363 GString *a = g_string_new("");
2364 gchar **spawnparts = split_quoted(origargs, FALSE);
2365 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2366 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2368 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2369 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2371 g_array_append_val(rets, a->str);
2372 g_string_free(a, FALSE);
2373 g_strfreev(spawnparts);
2375 gchar *origdup = g_strdup(origargs);
2376 g_array_append_val(rets, origdup);
2378 return (gchar**)g_array_free(rets, FALSE);
2382 run_handler (const gchar *act, const gchar *args) {
2383 /* Consider this code a temporary hack to make the handlers usable.
2384 In practice, all this splicing, injection, and reconstruction is
2385 inefficient, annoying and hard to manage. Potential pitfalls arise
2386 when the handler specific args 1) are not quoted (the handler
2387 callbacks should take care of this) 2) are quoted but interfere
2388 with the users' own quotation. A more ideal solution is
2389 to refactor parse_command so that it doesn't just take a string
2390 and execute it; rather than that, we should have a function which
2391 returns the argument vector parsed from the string. This vector
2392 could be modified (e.g. insert additional args into it) before
2393 passing it to the next function that actually executes it. Though
2394 it still isn't perfect for chain actions.. will reconsider & re-
2395 factor when I have the time. -duc */
2397 char **parts = g_strsplit(act, " ", 2);
2399 if (g_strcmp0(parts[0], "chain") == 0) {
2400 GString *newargs = g_string_new("");
2401 gchar **chainparts = split_quoted(parts[1], FALSE);
2403 /* for every argument in the chain, inject the handler args
2404 and make sure the new parts are wrapped in quotes */
2405 gchar **cp = chainparts;
2407 gchar *quotless = NULL;
2408 gchar **spliced_quotless = NULL; // sigh -_-;
2409 gchar **inpart = NULL;
2412 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2414 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2415 } else quotless = g_strdup(*cp);
2417 spliced_quotless = g_strsplit(quotless, " ", 2);
2418 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2419 g_strfreev(spliced_quotless);
2421 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2427 parse_command(parts[0], &(newargs->str[1]), NULL);
2428 g_string_free(newargs, TRUE);
2429 g_strfreev(chainparts);
2432 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2433 parse_command(inparts[0], inparts[1], NULL);
2441 add_binding (const gchar *key, const gchar *act) {
2442 char **parts = g_strsplit(act, " ", 2);
2449 if (uzbl.state.verbose)
2450 printf ("Binding %-10s : %s\n", key, act);
2451 action = new_action(parts[0], parts[1]);
2453 if (g_hash_table_remove (uzbl.bindings, key))
2454 g_warning ("Overwriting existing binding for \"%s\"", key);
2455 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2460 get_xdg_var (XDG_Var xdg) {
2461 const gchar* actual_value = getenv (xdg.environmental);
2462 const gchar* home = getenv ("HOME");
2463 gchar* return_value;
2465 if (! actual_value || strcmp (actual_value, "") == 0) {
2466 if (xdg.default_value) {
2467 return_value = str_replace ("~", home, xdg.default_value);
2469 return_value = NULL;
2472 return_value = str_replace("~", home, actual_value);
2475 return return_value;
2479 find_xdg_file (int xdg_type, char* filename) {
2480 /* xdg_type = 0 => config
2481 xdg_type = 1 => data
2482 xdg_type = 2 => cache*/
2484 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2485 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2488 gchar* temporary_string;
2492 if (! file_exists (temporary_file) && xdg_type != 2) {
2493 buf = get_xdg_var (XDG[3 + xdg_type]);
2494 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2497 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2498 g_free (temporary_file);
2499 temporary_file = g_strconcat (temporary_string, filename, NULL);
2503 //g_free (temporary_string); - segfaults.
2505 if (file_exists (temporary_file)) {
2506 return temporary_file;
2513 State *s = &uzbl.state;
2514 Network *n = &uzbl.net;
2516 for (i = 0; default_config[i].command != NULL; i++) {
2517 parse_cmd_line(default_config[i].command, NULL);
2520 if (g_strcmp0(s->config_file, "-") == 0) {
2521 s->config_file = NULL;
2525 else if (!s->config_file) {
2526 s->config_file = find_xdg_file (0, "/uzbl/config");
2529 if (s->config_file) {
2530 GArray* lines = read_file_by_line (s->config_file);
2534 while ((line = g_array_index(lines, gchar*, i))) {
2535 parse_cmd_line (line, NULL);
2539 g_array_free (lines, TRUE);
2541 if (uzbl.state.verbose)
2542 printf ("No configuration file loaded.\n");
2545 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2548 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2551 if (!uzbl.behave.cookie_handler)
2554 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2555 GString *s = g_string_new ("");
2556 SoupURI * soup_uri = soup_message_get_uri(msg);
2557 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2558 run_handler(uzbl.behave.cookie_handler, s->str);
2560 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2561 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2562 if ( p != NULL ) *p = '\0';
2563 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2565 if (uzbl.comm.sync_stdout)
2566 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2568 g_string_free(s, TRUE);
2572 save_cookies (SoupMessage *msg, gpointer user_data){
2576 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2577 cookie = soup_cookie_to_set_cookie_header(ck->data);
2578 SoupURI * soup_uri = soup_message_get_uri(msg);
2579 GString *s = g_string_new ("");
2580 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2581 run_handler(uzbl.behave.cookie_handler, s->str);
2583 g_string_free(s, TRUE);
2588 /* --- WEBINSPECTOR --- */
2590 hide_window_cb(GtkWidget *widget, gpointer data) {
2593 gtk_widget_hide(widget);
2597 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2600 (void) web_inspector;
2601 GtkWidget* scrolled_window;
2602 GtkWidget* new_web_view;
2605 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2606 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2607 G_CALLBACK(hide_window_cb), NULL);
2609 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2610 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2611 gtk_widget_show(g->inspector_window);
2613 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2614 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2615 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2616 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2617 gtk_widget_show(scrolled_window);
2619 new_web_view = webkit_web_view_new();
2620 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2622 return WEBKIT_WEB_VIEW(new_web_view);
2626 inspector_show_window_cb (WebKitWebInspector* inspector){
2628 gtk_widget_show(uzbl.gui.inspector_window);
2632 /* TODO: Add variables and code to make use of these functions */
2634 inspector_close_window_cb (WebKitWebInspector* inspector){
2640 inspector_attach_window_cb (WebKitWebInspector* inspector){
2646 inspector_detach_window_cb (WebKitWebInspector* inspector){
2652 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2658 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2664 set_up_inspector() {
2666 WebKitWebSettings *settings = view_settings();
2667 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2669 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2670 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2671 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2672 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2673 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2674 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2675 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2677 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2681 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2683 uzbl_cmdprop *c = v;
2688 if(c->type == TYPE_STR)
2689 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2690 else if(c->type == TYPE_INT)
2691 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2692 else if(c->type == TYPE_FLOAT)
2693 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2697 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2701 printf("bind %s = %s %s\n", (char *)k ,
2702 (char *)a->name, a->param?(char *)a->param:"");
2707 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2708 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2711 #ifndef UZBL_LIBRARY
2714 main (int argc, char* argv[]) {
2715 gtk_init (&argc, &argv);
2716 if (!g_thread_supported ())
2717 g_thread_init (NULL);
2718 uzbl.state.executable_path = g_strdup(argv[0]);
2719 uzbl.state.selected_url = NULL;
2720 uzbl.state.searchtx = NULL;
2722 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2723 g_option_context_add_main_entries (context, entries, NULL);
2724 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2725 g_option_context_parse (context, &argc, &argv, NULL);
2726 g_option_context_free(context);
2728 if (uzbl.behave.print_version) {
2729 printf("Commit: %s\n", COMMIT);
2733 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2734 if (argc > 1 && !uzbl.state.uri)
2735 uri_override = g_strdup(argv[1]);
2736 gboolean verbose_override = uzbl.state.verbose;
2738 /* initialize hash table */
2739 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2741 uzbl.net.soup_session = webkit_get_default_session();
2742 uzbl.state.keycmd = g_string_new("");
2744 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2745 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2746 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2747 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2748 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2749 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2752 if(uname(&uzbl.state.unameinfo) == -1)
2753 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2755 uzbl.gui.sbar.progress_s = g_strdup("=");
2756 uzbl.gui.sbar.progress_u = g_strdup("·");
2757 uzbl.gui.sbar.progress_w = 10;
2759 /* HTML mode defaults*/
2760 uzbl.behave.html_buffer = g_string_new("");
2761 uzbl.behave.html_endmarker = g_strdup(".");
2762 uzbl.behave.html_timeout = 60;
2763 uzbl.behave.base_url = g_strdup("http://invalid");
2765 /* default mode indicators */
2766 uzbl.behave.insert_indicator = g_strdup("I");
2767 uzbl.behave.cmd_indicator = g_strdup("C");
2771 make_var_to_name_hash();
2773 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2775 uzbl.gui.scrolled_win = create_browser();
2778 /* initial packing */
2779 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2780 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2782 if (uzbl.state.socket_id) {
2783 uzbl.gui.plug = create_plug ();
2784 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2785 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2787 uzbl.gui.main_window = create_window ();
2788 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2789 gtk_widget_show_all (uzbl.gui.main_window);
2790 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2793 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2795 if (uzbl.state.verbose) {
2796 printf("Uzbl start location: %s\n", argv[0]);
2797 if (uzbl.state.socket_id)
2798 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2800 printf("window_id %i\n",(int) uzbl.xwin);
2801 printf("pid %i\n", getpid ());
2802 printf("name: %s\n", uzbl.state.instance_name);
2805 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2806 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2807 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2808 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2809 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2813 if (!uzbl.behave.show_status)
2814 gtk_widget_hide(uzbl.gui.mainbar);
2821 if (verbose_override > uzbl.state.verbose)
2822 uzbl.state.verbose = verbose_override;
2825 set_var_value("uri", uri_override);
2826 g_free(uri_override);
2827 } else if (uzbl.state.uri)
2828 cmd_load_uri(uzbl.gui.web_view, NULL);
2833 return EXIT_SUCCESS;
2837 /* vi: set et ts=4: */