1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
79 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
81 "Print the version and exit", NULL },
82 { NULL, 0, 0, 0, NULL, NULL, NULL }
85 /* associate command names to their properties */
86 typedef const struct {
93 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
95 /* an abbreviation to help keep the table's width humane */
96 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
101 } var_name_to_ptr[] = {
102 /* variable name pointer to variable in code type dump callback function */
103 /* --------------------------------------------------------------------------------------- */
104 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
105 { "geometry", PTR(uzbl.gui.geometry, STR, 1, cmd_set_geometry)},
106 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
107 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
108 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
109 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
110 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
111 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
112 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
113 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
114 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
115 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
116 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
117 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
118 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
119 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
120 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
121 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
122 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
123 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
124 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
125 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
126 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
127 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
128 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
129 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
130 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
131 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
132 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
133 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
134 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
135 { "new_window", PTR(uzbl.behave.new_window, STR, 1, cmd_new_window)},
136 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
137 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
138 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
139 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
140 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
141 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
142 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
143 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
144 /* exported WebKitWebSettings properties */
145 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
146 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
147 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
148 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
149 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
150 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
151 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
152 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
153 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
154 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
155 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
156 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
157 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
158 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
159 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
160 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
162 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
163 }, *n2v_p = var_name_to_ptr;
169 { "SHIFT", GDK_SHIFT_MASK }, // shift
170 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
171 { "CONTROL", GDK_CONTROL_MASK }, // control
172 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
173 { "MOD2", GDK_MOD2_MASK }, // 5th mod
174 { "MOD3", GDK_MOD3_MASK }, // 6th mod
175 { "MOD4", GDK_MOD4_MASK }, // 7th mod
176 { "MOD5", GDK_MOD5_MASK }, // 8th mod
177 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
178 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
179 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
180 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
181 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
182 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
183 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
184 { "META", GDK_META_MASK }, // meta (since 2.10)
189 /* construct a hash from the var_name_to_ptr array for quick access */
191 make_var_to_name_hash() {
192 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
194 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
199 /* --- UTILITY FUNCTIONS --- */
201 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
203 get_exp_type(gchar *s) {
207 else if(*(s+1) == '{')
208 return EXP_BRACED_VAR;
209 else if(*(s+1) == '<')
212 return EXP_SIMPLE_VAR;
218 * recurse == 1: don't expand '@(command)@'
219 * recurse == 2: don't expand '@<java script>@'
222 expand(char *s, guint recurse) {
226 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
231 gchar *cmd_stdout = NULL;
233 GString *buf = g_string_new("");
234 GString *js_ret = g_string_new("");
239 g_string_append_c(buf, *++s);
244 etype = get_exp_type(s);
249 if( (vend = strpbrk(s, end_simple_var)) ||
250 (vend = strchr(s, '\0')) ) {
251 strncpy(ret, s, vend-s);
257 if( (vend = strchr(s, upto)) ||
258 (vend = strchr(s, '\0')) ) {
259 strncpy(ret, s, vend-s);
265 strcpy(str_end, ")@");
267 if( (vend = strstr(s, str_end)) ||
268 (vend = strchr(s, '\0')) ) {
269 strncpy(ret, s, vend-s);
275 strcpy(str_end, ">@");
277 if( (vend = strstr(s, str_end)) ||
278 (vend = strchr(s, '\0')) ) {
279 strncpy(ret, s, vend-s);
285 if(etype == EXP_SIMPLE_VAR ||
286 etype == EXP_BRACED_VAR) {
287 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
288 if(c->type == TYPE_STR)
289 g_string_append(buf, (gchar *)*c->ptr);
290 else if(c->type == TYPE_INT) {
291 char *b = itos((int)*c->ptr);
292 g_string_append(buf, b);
296 if(etype == EXP_SIMPLE_VAR)
301 else if(recurse != 1 &&
303 mycmd = expand(ret, 1);
304 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
308 g_printerr("error on running command: %s\n", err->message);
311 else if (*cmd_stdout) {
312 g_string_append(buf, cmd_stdout);
317 else if(recurse != 2 &&
319 mycmd = expand(ret, 2);
320 eval_js(uzbl.gui.web_view, mycmd, js_ret);
324 g_string_append(buf, js_ret->str);
325 g_string_free(js_ret, TRUE);
326 js_ret = g_string_new("");
333 g_string_append_c(buf, *s);
338 g_string_free(js_ret, TRUE);
339 return g_string_free(buf, FALSE);
346 snprintf(tmp, sizeof(tmp), "%i", val);
347 return g_strdup(tmp);
351 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
354 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
357 str_replace (const char* search, const char* replace, const char* string) {
361 buf = g_strsplit (string, search, -1);
362 ret = g_strjoinv (replace, buf);
363 g_strfreev(buf); // somebody said this segfaults
369 read_file_by_line (gchar *path) {
370 GIOChannel *chan = NULL;
371 gchar *readbuf = NULL;
373 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
376 chan = g_io_channel_new_file(path, "r", NULL);
379 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
380 const gchar* val = g_strdup (readbuf);
381 g_array_append_val (lines, val);
386 g_io_channel_unref (chan);
388 fprintf(stderr, "File '%s' not be read.\n", path);
395 gchar* parseenv (char* string) {
396 extern char** environ;
397 gchar* tmpstr = NULL;
401 while (environ[i] != NULL) {
402 gchar** env = g_strsplit (environ[i], "=", 2);
403 gchar* envname = g_strconcat ("$", env[0], NULL);
405 if (g_strrstr (string, envname) != NULL) {
406 tmpstr = g_strdup(string);
408 string = str_replace(envname, env[1], tmpstr);
413 g_strfreev (env); // somebody said this breaks uzbl
421 setup_signal(int signr, sigfunc *shandler) {
422 struct sigaction nh, oh;
424 nh.sa_handler = shandler;
425 sigemptyset(&nh.sa_mask);
428 if(sigaction(signr, &nh, &oh) < 0)
436 if (uzbl.behave.fifo_dir)
437 unlink (uzbl.comm.fifo_path);
438 if (uzbl.behave.socket_dir)
439 unlink (uzbl.comm.socket_path);
441 g_free(uzbl.state.executable_path);
442 g_string_free(uzbl.state.keycmd, TRUE);
443 g_hash_table_destroy(uzbl.bindings);
444 g_hash_table_destroy(uzbl.behave.commands);
447 /* used for html_mode_timeout
448 * be sure to extend this function to use
449 * more timers if needed in other places
452 set_timeout(int seconds) {
454 memset(&t, 0, sizeof t);
456 t.it_value.tv_sec = seconds;
457 t.it_value.tv_usec = 0;
458 setitimer(ITIMER_REAL, &t, NULL);
461 /* --- SIGNAL HANDLER --- */
464 catch_sigterm(int s) {
470 catch_sigint(int s) {
480 set_var_value("mode", "0");
485 /* --- CALLBACKS --- */
488 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
491 (void) navigation_action;
492 (void) policy_decision;
494 const gchar* uri = webkit_network_request_get_uri (request);
495 if (uzbl.state.verbose)
496 printf("New window requested -> %s \n", uri);
497 webkit_web_policy_decision_use(policy_decision);
502 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
507 /* If we can display it, let's display it... */
508 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
509 webkit_web_policy_decision_use (policy_decision);
513 /* ...everything we can't displayed is downloaded */
514 webkit_web_policy_decision_download (policy_decision);
519 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
523 if (uzbl.state.selected_url != NULL) {
524 if (uzbl.state.verbose)
525 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
526 new_window_load_uri(uzbl.state.selected_url);
528 if (uzbl.state.verbose)
529 printf("New web view -> %s\n","Nothing to open, exiting");
535 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
538 if (uzbl.behave.download_handler) {
539 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
540 if (uzbl.state.verbose)
541 printf("Download -> %s\n",uri);
542 /* if urls not escaped, we may have to escape and quote uri before this call */
543 run_handler(uzbl.behave.download_handler, uri);
548 /* scroll a bar in a given direction */
550 scroll (GtkAdjustment* bar, GArray *argv) {
554 gdouble page_size = gtk_adjustment_get_page_size(bar);
555 gdouble value = gtk_adjustment_get_value(bar);
556 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
559 value += page_size * amount * 0.01;
563 max_value = gtk_adjustment_get_upper(bar) - page_size;
565 if (value > max_value)
566 value = max_value; /* don't scroll past the end of the page */
568 gtk_adjustment_set_value (bar, value);
572 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
573 (void) page; (void) argv; (void) result;
574 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
578 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
579 (void) page; (void) argv; (void) result;
580 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
581 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
585 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
586 (void) page; (void) result;
587 scroll(uzbl.gui.bar_v, argv);
591 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
592 (void) page; (void) result;
593 scroll(uzbl.gui.bar_h, argv);
598 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
599 if(uzbl.state.verbose)
600 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
602 /* update geometry var with the actual geometry
603 this is necessary as some WMs don't seem to honour
604 the above setting and we don't want to end up with
605 wrong geometry information
612 if (!uzbl.behave.show_status) {
613 gtk_widget_hide(uzbl.gui.mainbar);
615 gtk_widget_show(uzbl.gui.mainbar);
621 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
626 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
630 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
635 if (uzbl.behave.show_status) {
636 gtk_widget_hide(uzbl.gui.mainbar);
638 gtk_widget_show(uzbl.gui.mainbar);
640 uzbl.behave.show_status = !uzbl.behave.show_status;
645 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
649 //Set selected_url state variable
650 g_free(uzbl.state.selected_url);
651 uzbl.state.selected_url = NULL;
653 uzbl.state.selected_url = g_strdup(link);
659 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
662 const gchar *title = webkit_web_view_get_title(web_view);
663 if (uzbl.gui.main_title)
664 g_free (uzbl.gui.main_title);
665 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
670 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
673 uzbl.gui.sbar.load_progress = progress;
678 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
682 if (uzbl.behave.load_finish_handler)
683 run_handler(uzbl.behave.load_finish_handler, "");
687 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
691 uzbl.gui.sbar.load_progress = 0;
692 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
693 if (uzbl.behave.load_start_handler)
694 run_handler(uzbl.behave.load_start_handler, "");
698 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
701 g_free (uzbl.state.uri);
702 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
703 uzbl.state.uri = g_string_free (newuri, FALSE);
704 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
705 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
708 if (uzbl.behave.load_commit_handler)
709 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
713 destroy_cb (GtkWidget* widget, gpointer data) {
721 if (uzbl.behave.history_handler) {
723 struct tm * timeinfo;
726 timeinfo = localtime ( &rawtime );
727 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
728 run_handler(uzbl.behave.history_handler, date);
733 /* VIEW funcs (little webkit wrappers) */
734 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
736 VIEWFUNC(reload_bypass_cache)
737 VIEWFUNC(stop_loading)
744 /* -- command to callback/function map for things we cannot attach to any signals */
745 static struct {char *key; CommandInfo value;} cmdlist[] =
746 { /* key function no_split */
747 { "back", {view_go_back, 0} },
748 { "forward", {view_go_forward, 0} },
749 { "scroll_vert", {scroll_vert, 0} },
750 { "scroll_horz", {scroll_horz, 0} },
751 { "scroll_begin", {scroll_begin, 0} },
752 { "scroll_end", {scroll_end, 0} },
753 { "reload", {view_reload, 0}, },
754 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
755 { "stop", {view_stop_loading, 0}, },
756 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
757 { "zoom_out", {view_zoom_out, 0}, },
758 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
759 { "uri", {load_uri, TRUE} },
760 { "js", {run_js, TRUE} },
761 { "script", {run_external_js, 0} },
762 { "toggle_status", {toggle_status_cb, 0} },
763 { "spawn", {spawn, 0} },
764 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
765 { "sh", {spawn_sh, 0} },
766 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
767 { "exit", {close_uzbl, 0} },
768 { "search", {search_forward_text, TRUE} },
769 { "search_reverse", {search_reverse_text, TRUE} },
770 { "dehilight", {dehilight, 0} },
771 { "toggle_insert_mode", {toggle_insert_mode, 0} },
772 { "set", {set_var, TRUE} },
773 //{ "get", {get_var, TRUE} },
774 { "bind", {act_bind, TRUE} },
775 { "dump_config", {act_dump_config, 0} },
776 { "keycmd", {keycmd, TRUE} },
777 { "keycmd_nl", {keycmd_nl, TRUE} },
778 { "keycmd_bs", {keycmd_bs, 0} },
779 { "chain", {chain, 0} },
780 { "print", {print, TRUE} }
787 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
789 for (i = 0; i < LENGTH(cmdlist); i++)
790 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
793 /* -- CORE FUNCTIONS -- */
796 free_action(gpointer act) {
797 Action *action = (Action*)act;
798 g_free(action->name);
800 g_free(action->param);
805 new_action(const gchar *name, const gchar *param) {
806 Action *action = g_new(Action, 1);
808 action->name = g_strdup(name);
810 action->param = g_strdup(param);
812 action->param = NULL;
818 file_exists (const char * filename) {
819 return (access(filename, F_OK) == 0);
823 set_var(WebKitWebView *page, GArray *argv, GString *result) {
824 (void) page; (void) result;
825 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
826 if (split[0] != NULL) {
827 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
828 set_var_value(g_strstrip(split[0]), value);
835 print(WebKitWebView *page, GArray *argv, GString *result) {
836 (void) page; (void) result;
839 buf = expand(argv_idx(argv, 0), 0);
840 g_string_assign(result, buf);
845 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
846 (void) page; (void) result;
847 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
848 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
849 add_binding(g_strstrip(split[0]), value);
861 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
862 (void) page; (void) result;
864 if (argv_idx(argv, 0)) {
865 if (strcmp (argv_idx(argv, 0), "0") == 0) {
866 uzbl.behave.insert_mode = FALSE;
868 uzbl.behave.insert_mode = TRUE;
871 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
878 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
881 if (argv_idx(argv, 0)) {
882 GString* newuri = g_string_new (argv_idx(argv, 0));
883 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
884 run_js(web_view, argv, NULL);
887 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
888 g_string_prepend (newuri, "http://");
889 /* if we do handle cookies, ask our handler for them */
890 webkit_web_view_load_uri (web_view, newuri->str);
891 g_string_free (newuri, TRUE);
899 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
900 size_t argumentCount, const JSValueRef arguments[],
901 JSValueRef* exception) {
906 JSStringRef js_result_string;
907 GString *result = g_string_new("");
909 if (argumentCount >= 1) {
910 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
911 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
912 char ctl_line[arg_size];
913 JSStringGetUTF8CString(arg, ctl_line, arg_size);
915 parse_cmd_line(ctl_line, result);
917 JSStringRelease(arg);
919 js_result_string = JSStringCreateWithUTF8CString(result->str);
921 g_string_free(result, TRUE);
923 return JSValueMakeString(ctx, js_result_string);
926 static JSStaticFunction js_static_functions[] = {
927 {"run", js_run_command, kJSPropertyAttributeNone},
932 /* This function creates the class and its definition, only once */
933 if (!uzbl.js.initialized) {
934 /* it would be pretty cool to make this dynamic */
935 uzbl.js.classdef = kJSClassDefinitionEmpty;
936 uzbl.js.classdef.staticFunctions = js_static_functions;
938 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
944 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
945 WebKitWebFrame *frame;
946 JSGlobalContextRef context;
947 JSObjectRef globalobject;
948 JSStringRef var_name;
950 JSStringRef js_script;
951 JSValueRef js_result;
952 JSStringRef js_result_string;
953 size_t js_result_size;
957 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
958 context = webkit_web_frame_get_global_context(frame);
959 globalobject = JSContextGetGlobalObject(context);
961 /* uzbl javascript namespace */
962 var_name = JSStringCreateWithUTF8CString("Uzbl");
963 JSObjectSetProperty(context, globalobject, var_name,
964 JSObjectMake(context, uzbl.js.classref, NULL),
965 kJSClassAttributeNone, NULL);
967 /* evaluate the script and get return value*/
968 js_script = JSStringCreateWithUTF8CString(script);
969 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
970 if (js_result && !JSValueIsUndefined(context, js_result)) {
971 js_result_string = JSValueToStringCopy(context, js_result, NULL);
972 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
974 if (js_result_size) {
975 char js_result_utf8[js_result_size];
976 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
977 g_string_assign(result, js_result_utf8);
980 JSStringRelease(js_result_string);
984 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
986 JSStringRelease(var_name);
987 JSStringRelease(js_script);
991 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
993 if (argv_idx(argv, 0))
994 eval_js(web_view, argv_idx(argv, 0), result);
998 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1000 if (argv_idx(argv, 0)) {
1001 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1006 while ((line = g_array_index(lines, gchar*, i))) {
1008 js = g_strdup (line);
1010 gchar* newjs = g_strconcat (js, line, NULL);
1017 if (uzbl.state.verbose)
1018 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1020 if (argv_idx (argv, 1)) {
1021 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1025 eval_js (web_view, js, result);
1027 g_array_free (lines, TRUE);
1032 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1033 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1034 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1035 webkit_web_view_unmark_text_matches (page);
1036 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1037 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1041 if (uzbl.state.searchtx) {
1042 if (uzbl.state.verbose)
1043 printf ("Searching: %s\n", uzbl.state.searchtx);
1044 webkit_web_view_set_highlight_text_matches (page, TRUE);
1045 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1050 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1052 search_text(page, argv, TRUE);
1056 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1058 search_text(page, argv, FALSE);
1062 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1063 (void) argv; (void) result;
1064 webkit_web_view_set_highlight_text_matches (page, FALSE);
1069 new_window_load_uri (const gchar * uri) {
1070 if (uzbl.behave.new_window) {
1071 GString *s = g_string_new ("");
1072 g_string_printf(s, "'%s'", uri);
1073 run_handler(uzbl.behave.new_window, s->str);
1076 GString* to_execute = g_string_new ("");
1077 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1079 for (i = 0; entries[i].long_name != NULL; i++) {
1080 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1081 gchar** str = (gchar**)entries[i].arg_data;
1083 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1087 if (uzbl.state.verbose)
1088 printf("\n%s\n", to_execute->str);
1089 g_spawn_command_line_async (to_execute->str, NULL);
1090 g_string_free (to_execute, TRUE);
1094 chain (WebKitWebView *page, GArray *argv, GString *result) {
1095 (void) page; (void) result;
1097 gchar **parts = NULL;
1099 while ((a = argv_idx(argv, i++))) {
1100 parts = g_strsplit (a, " ", 2);
1101 parse_command(parts[0], parts[1], result);
1107 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1111 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1117 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1121 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1127 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1132 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1134 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1139 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1146 /* --Statusbar functions-- */
1148 build_progressbar_ascii(int percent) {
1149 int width=uzbl.gui.sbar.progress_w;
1152 GString *bar = g_string_new("");
1154 l = (double)percent*((double)width/100.);
1155 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1157 for(i=0; i<(int)l; i++)
1158 g_string_append(bar, uzbl.gui.sbar.progress_s);
1161 g_string_append(bar, uzbl.gui.sbar.progress_u);
1163 return g_string_free(bar, FALSE);
1168 const GScannerConfig scan_config = {
1171 ) /* cset_skip_characters */,
1176 ) /* cset_identifier_first */,
1183 ) /* cset_identifier_nth */,
1184 ( "" ) /* cpair_comment_single */,
1186 TRUE /* case_sensitive */,
1188 FALSE /* skip_comment_multi */,
1189 FALSE /* skip_comment_single */,
1190 FALSE /* scan_comment_multi */,
1191 TRUE /* scan_identifier */,
1192 TRUE /* scan_identifier_1char */,
1193 FALSE /* scan_identifier_NULL */,
1194 TRUE /* scan_symbols */,
1195 FALSE /* scan_binary */,
1196 FALSE /* scan_octal */,
1197 FALSE /* scan_float */,
1198 FALSE /* scan_hex */,
1199 FALSE /* scan_hex_dollar */,
1200 FALSE /* scan_string_sq */,
1201 FALSE /* scan_string_dq */,
1202 TRUE /* numbers_2_int */,
1203 FALSE /* int_2_float */,
1204 FALSE /* identifier_2_string */,
1205 FALSE /* char_2_token */,
1206 FALSE /* symbol_2_token */,
1207 TRUE /* scope_0_fallback */,
1212 uzbl.scan = g_scanner_new(&scan_config);
1213 while(symp->symbol_name) {
1214 g_scanner_scope_add_symbol(uzbl.scan, 0,
1216 GINT_TO_POINTER(symp->symbol_token));
1222 expand_template(const char *template, gboolean escape_markup) {
1223 if(!template) return NULL;
1225 GTokenType token = G_TOKEN_NONE;
1226 GString *ret = g_string_new("");
1230 g_scanner_input_text(uzbl.scan, template, strlen(template));
1231 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1232 token = g_scanner_get_next_token(uzbl.scan);
1234 if(token == G_TOKEN_SYMBOL) {
1235 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1239 buf = uzbl.state.uri?
1240 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1241 g_string_append(ret, buf);
1245 g_string_append(ret, uzbl.state.uri?
1246 uzbl.state.uri:g_strdup(""));
1249 buf = itos(uzbl.gui.sbar.load_progress);
1250 g_string_append(ret, buf);
1253 case SYM_LOADPRGSBAR:
1254 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1255 g_string_append(ret, buf);
1260 buf = uzbl.gui.main_title?
1261 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1262 g_string_append(ret, buf);
1266 g_string_append(ret, uzbl.gui.main_title?
1267 uzbl.gui.main_title:g_strdup(""));
1269 case SYM_SELECTED_URI:
1271 buf = uzbl.state.selected_url?
1272 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1273 g_string_append(ret, buf);
1277 g_string_append(ret, uzbl.state.selected_url?
1278 uzbl.state.selected_url:g_strdup(""));
1281 buf = itos(uzbl.xwin);
1282 g_string_append(ret,
1283 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1288 buf = uzbl.state.keycmd->str?
1289 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1290 g_string_append(ret, buf);
1294 g_string_append(ret, uzbl.state.keycmd->str?
1295 uzbl.state.keycmd->str:g_strdup(""));
1298 g_string_append(ret,
1299 uzbl.behave.insert_mode?
1300 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1303 g_string_append(ret,
1304 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1306 /* useragent syms */
1308 buf = itos(WEBKIT_MAJOR_VERSION);
1309 g_string_append(ret, buf);
1313 buf = itos(WEBKIT_MINOR_VERSION);
1314 g_string_append(ret, buf);
1318 buf = itos(WEBKIT_MICRO_VERSION);
1319 g_string_append(ret, buf);
1323 g_string_append(ret, uzbl.state.unameinfo.sysname);
1326 g_string_append(ret, uzbl.state.unameinfo.nodename);
1329 g_string_append(ret, uzbl.state.unameinfo.release);
1332 g_string_append(ret, uzbl.state.unameinfo.version);
1335 g_string_append(ret, uzbl.state.unameinfo.machine);
1338 g_string_append(ret, ARCH);
1341 case SYM_DOMAINNAME:
1342 g_string_append(ret, uzbl.state.unameinfo.domainname);
1346 g_string_append(ret, COMMIT);
1352 else if(token == G_TOKEN_INT) {
1353 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1354 g_string_append(ret, buf);
1357 else if(token == G_TOKEN_IDENTIFIER) {
1358 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1360 else if(token == G_TOKEN_CHAR) {
1361 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1365 return g_string_free(ret, FALSE);
1367 /* --End Statusbar functions-- */
1370 sharg_append(GArray *a, const gchar *str) {
1371 const gchar *s = (str ? str : "");
1372 g_array_append_val(a, s);
1375 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1377 run_command (const gchar *command, const guint npre, const gchar **args,
1378 const gboolean sync, char **output_stdout) {
1379 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1382 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1383 gchar *pid = itos(getpid());
1384 gchar *xwin = itos(uzbl.xwin);
1386 sharg_append(a, command);
1387 for (i = 0; i < npre; i++) /* add n args before the default vars */
1388 sharg_append(a, args[i]);
1389 sharg_append(a, uzbl.state.config_file);
1390 sharg_append(a, pid);
1391 sharg_append(a, xwin);
1392 sharg_append(a, uzbl.comm.fifo_path);
1393 sharg_append(a, uzbl.comm.socket_path);
1394 sharg_append(a, uzbl.state.uri);
1395 sharg_append(a, uzbl.gui.main_title);
1397 for (i = npre; i < g_strv_length((gchar**)args); i++)
1398 sharg_append(a, args[i]);
1402 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1404 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1405 NULL, NULL, output_stdout, NULL, NULL, &err);
1406 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1407 NULL, NULL, NULL, &err);
1409 if (uzbl.state.verbose) {
1410 GString *s = g_string_new("spawned:");
1411 for (i = 0; i < (a->len); i++) {
1412 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1413 g_string_append_printf(s, " %s", qarg);
1416 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1417 printf("%s\n", s->str);
1418 g_string_free(s, TRUE);
1420 printf("Stdout: %s\n", *output_stdout);
1424 g_printerr("error on run_command: %s\n", err->message);
1429 g_array_free (a, TRUE);
1434 split_quoted(const gchar* src, const gboolean unquote) {
1435 /* split on unquoted space, return array of strings;
1436 remove a layer of quotes and backslashes if unquote */
1437 if (!src) return NULL;
1439 gboolean dq = FALSE;
1440 gboolean sq = FALSE;
1441 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1442 GString *s = g_string_new ("");
1446 for (p = src; *p != '\0'; p++) {
1447 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1448 else if (*p == '\\') { g_string_append_c(s, *p++);
1449 g_string_append_c(s, *p); }
1450 else if ((*p == '"') && unquote && !sq) dq = !dq;
1451 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1453 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1454 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1456 else if ((*p == ' ') && !dq && !sq) {
1457 dup = g_strdup(s->str);
1458 g_array_append_val(a, dup);
1459 g_string_truncate(s, 0);
1460 } else g_string_append_c(s, *p);
1462 dup = g_strdup(s->str);
1463 g_array_append_val(a, dup);
1464 ret = (gchar**)a->data;
1465 g_array_free (a, FALSE);
1466 g_string_free (s, TRUE);
1471 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1472 (void)web_view; (void)result;
1473 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1474 if (argv_idx(argv, 0))
1475 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1479 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1480 (void)web_view; (void)result;
1482 if (argv_idx(argv, 0))
1483 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1484 TRUE, &uzbl.comm.sync_stdout);
1488 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1489 (void)web_view; (void)result;
1490 if (!uzbl.behave.shell_cmd) {
1491 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1496 gchar *spacer = g_strdup("");
1497 g_array_insert_val(argv, 1, spacer);
1498 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1500 for (i = 1; i < g_strv_length(cmd); i++)
1501 g_array_prepend_val(argv, cmd[i]);
1503 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1509 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1510 (void)web_view; (void)result;
1511 if (!uzbl.behave.shell_cmd) {
1512 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1517 gchar *spacer = g_strdup("");
1518 g_array_insert_val(argv, 1, spacer);
1519 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1521 for (i = 1; i < g_strv_length(cmd); i++)
1522 g_array_prepend_val(argv, cmd[i]);
1524 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1525 TRUE, &uzbl.comm.sync_stdout);
1531 parse_command(const char *cmd, const char *param, GString *result) {
1534 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1536 gchar **par = split_quoted(param, TRUE);
1537 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1539 if (c->no_split) { /* don't split */
1540 sharg_append(a, param);
1542 for (i = 0; i < g_strv_length(par); i++)
1543 sharg_append(a, par[i]);
1546 if (result == NULL) {
1547 GString *result_print = g_string_new("");
1549 c->function(uzbl.gui.web_view, a, result_print);
1550 if (result_print->len)
1551 printf("%*s\n", result_print->len, result_print->str);
1553 g_string_free(result_print, TRUE);
1555 c->function(uzbl.gui.web_view, a, result);
1558 g_array_free (a, TRUE);
1561 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1568 if(*uzbl.net.proxy_url == ' '
1569 || uzbl.net.proxy_url == NULL) {
1570 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1571 (GType) SOUP_SESSION_PROXY_URI);
1574 suri = soup_uri_new(uzbl.net.proxy_url);
1575 g_object_set(G_OBJECT(uzbl.net.soup_session),
1576 SOUP_SESSION_PROXY_URI,
1578 soup_uri_free(suri);
1585 if(file_exists(uzbl.gui.icon)) {
1586 if (uzbl.gui.main_window)
1587 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1589 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1595 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1596 g_array_append_val (a, uzbl.state.uri);
1597 load_uri(uzbl.gui.web_view, a, NULL);
1598 g_array_free (a, TRUE);
1602 cmd_always_insert_mode() {
1603 uzbl.behave.insert_mode =
1604 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1610 g_object_set(G_OBJECT(uzbl.net.soup_session),
1611 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1615 cmd_max_conns_host() {
1616 g_object_set(G_OBJECT(uzbl.net.soup_session),
1617 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1622 soup_session_remove_feature
1623 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1624 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1625 /*g_free(uzbl.net.soup_logger);*/
1627 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1628 soup_session_add_feature(uzbl.net.soup_session,
1629 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1632 static WebKitWebSettings*
1634 return webkit_web_view_get_settings(uzbl.gui.web_view);
1639 WebKitWebSettings *ws = view_settings();
1640 if (uzbl.behave.font_size > 0) {
1641 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1644 if (uzbl.behave.monospace_size > 0) {
1645 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1646 uzbl.behave.monospace_size, NULL);
1648 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1649 uzbl.behave.font_size, NULL);
1655 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1659 cmd_disable_plugins() {
1660 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1661 !uzbl.behave.disable_plugins, NULL);
1665 cmd_disable_scripts() {
1666 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1667 !uzbl.behave.disable_scripts, NULL);
1671 cmd_minimum_font_size() {
1672 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1673 uzbl.behave.minimum_font_size, NULL);
1676 cmd_autoload_img() {
1677 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1678 uzbl.behave.autoload_img, NULL);
1683 cmd_autoshrink_img() {
1684 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1685 uzbl.behave.autoshrink_img, NULL);
1690 cmd_enable_spellcheck() {
1691 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1692 uzbl.behave.enable_spellcheck, NULL);
1696 cmd_enable_private() {
1697 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1698 uzbl.behave.enable_private, NULL);
1703 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1704 uzbl.behave.print_bg, NULL);
1709 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1710 uzbl.behave.style_uri, NULL);
1714 cmd_resizable_txt() {
1715 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1716 uzbl.behave.resizable_txt, NULL);
1720 cmd_default_encoding() {
1721 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1722 uzbl.behave.default_encoding, NULL);
1726 cmd_enforce_96dpi() {
1727 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1728 uzbl.behave.enforce_96dpi, NULL);
1732 cmd_caret_browsing() {
1733 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1734 uzbl.behave.caret_browsing, NULL);
1738 cmd_cookie_handler() {
1739 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 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.cookie_handler);
1744 uzbl.behave.cookie_handler =
1745 g_strdup_printf("sync_%s %s", split[0], split[1]);
1752 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1753 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1754 if ((g_strcmp0(split[0], "sh") == 0) ||
1755 (g_strcmp0(split[0], "spawn") == 0)) {
1756 g_free (uzbl.behave.new_window);
1757 uzbl.behave.new_window =
1758 g_strdup_printf("%s %s", split[0], split[1]);
1765 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1770 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1775 if(uzbl.behave.inject_html) {
1776 webkit_web_view_load_html_string (uzbl.gui.web_view,
1777 uzbl.behave.inject_html, NULL);
1786 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1787 uzbl.behave.modmask = 0;
1789 if(uzbl.behave.modkey)
1790 g_free(uzbl.behave.modkey);
1791 uzbl.behave.modkey = buf;
1793 for (i = 0; modkeys[i].key != NULL; i++) {
1794 if (g_strrstr(buf, modkeys[i].key))
1795 uzbl.behave.modmask |= modkeys[i].mask;
1801 if (*uzbl.net.useragent == ' ') {
1802 g_free (uzbl.net.useragent);
1803 uzbl.net.useragent = NULL;
1805 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1807 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1808 g_free(uzbl.net.useragent);
1809 uzbl.net.useragent = ua;
1815 gtk_widget_ref(uzbl.gui.scrolled_win);
1816 gtk_widget_ref(uzbl.gui.mainbar);
1817 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1818 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1820 if(uzbl.behave.status_top) {
1821 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1822 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1825 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1826 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1828 gtk_widget_unref(uzbl.gui.scrolled_win);
1829 gtk_widget_unref(uzbl.gui.mainbar);
1830 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1835 set_var_value(gchar *name, gchar *val) {
1836 uzbl_cmdprop *c = NULL;
1840 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1841 /* check for the variable type */
1842 if (c->type == TYPE_STR) {
1843 buf = expand(val, 0);
1846 } else if(c->type == TYPE_INT) {
1847 int *ip = (int *)c->ptr;
1848 buf = expand(val, 0);
1849 *ip = (int)strtoul(buf, &endp, 10);
1851 } else if (c->type == TYPE_FLOAT) {
1852 float *fp = (float *)c->ptr;
1853 buf = expand(val, 0);
1854 *fp = strtod(buf, &endp);
1858 /* invoke a command specific function */
1859 if(c->func) c->func();
1866 Behaviour *b = &uzbl.behave;
1868 if(b->html_buffer->str) {
1869 webkit_web_view_load_html_string (uzbl.gui.web_view,
1870 b->html_buffer->str, b->base_url);
1871 g_string_free(b->html_buffer, TRUE);
1872 b->html_buffer = g_string_new("");
1876 enum {M_CMD, M_HTML};
1878 parse_cmd_line(const char *ctl_line, GString *result) {
1879 Behaviour *b = &uzbl.behave;
1882 if(b->mode == M_HTML) {
1883 len = strlen(b->html_endmarker);
1884 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1885 if(len == strlen(ctl_line)-1 &&
1886 !strncmp(b->html_endmarker, ctl_line, len)) {
1888 set_var_value("mode", "0");
1893 set_timeout(b->html_timeout);
1894 g_string_append(b->html_buffer, ctl_line);
1897 else if((ctl_line[0] == '#') /* Comments */
1898 || (ctl_line[0] == ' ')
1899 || (ctl_line[0] == '\n'))
1900 ; /* ignore these lines */
1901 else { /* parse a command */
1903 gchar **tokens = NULL;
1904 len = strlen(ctl_line);
1906 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1907 ctlstrip = g_strndup(ctl_line, len - 1);
1908 else ctlstrip = g_strdup(ctl_line);
1910 tokens = g_strsplit(ctlstrip, " ", 2);
1911 parse_command(tokens[0], tokens[1], result);
1918 build_stream_name(int type, const gchar* dir) {
1919 char *xwin_str = NULL;
1920 State *s = &uzbl.state;
1923 xwin_str = itos((int)uzbl.xwin);
1925 str = g_strdup_printf
1926 ("%s/uzbl_fifo_%s", dir,
1927 s->instance_name ? s->instance_name : xwin_str);
1928 } else if (type == SOCKET) {
1929 str = g_strdup_printf
1930 ("%s/uzbl_socket_%s", dir,
1931 s->instance_name ? s->instance_name : xwin_str );
1938 control_fifo(GIOChannel *gio, GIOCondition condition) {
1939 if (uzbl.state.verbose)
1940 printf("triggered\n");
1945 if (condition & G_IO_HUP)
1946 g_error ("Fifo: Read end of pipe died!\n");
1949 g_error ("Fifo: GIOChannel broke\n");
1951 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1952 if (ret == G_IO_STATUS_ERROR) {
1953 g_error ("Fifo: Error reading: %s\n", err->message);
1957 parse_cmd_line(ctl_line, NULL);
1964 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1965 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1966 if (unlink(uzbl.comm.fifo_path) == -1)
1967 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1968 g_free(uzbl.comm.fifo_path);
1969 uzbl.comm.fifo_path = NULL;
1972 if (*dir == ' ') { /* space unsets the variable */
1977 GIOChannel *chan = NULL;
1978 GError *error = NULL;
1979 gchar *path = build_stream_name(FIFO, dir);
1981 if (!file_exists(path)) {
1982 if (mkfifo (path, 0666) == 0) {
1983 // 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.
1984 chan = g_io_channel_new_file(path, "r+", &error);
1986 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1987 if (uzbl.state.verbose)
1988 printf ("init_fifo: created successfully as %s\n", path);
1989 uzbl.comm.fifo_path = path;
1991 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1992 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1993 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1994 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1996 /* if we got this far, there was an error; cleanup */
1997 if (error) g_error_free (error);
2004 control_stdin(GIOChannel *gio, GIOCondition condition) {
2006 gchar *ctl_line = NULL;
2009 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
2010 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
2013 parse_cmd_line(ctl_line, NULL);
2021 GIOChannel *chan = NULL;
2022 GError *error = NULL;
2024 chan = g_io_channel_unix_new(fileno(stdin));
2026 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2027 g_error ("Stdin: could not add watch\n");
2029 if (uzbl.state.verbose)
2030 printf ("Stdin: watch added successfully\n");
2033 g_error ("Stdin: Error while opening: %s\n", error->message);
2035 if (error) g_error_free (error);
2039 control_socket(GIOChannel *chan) {
2040 struct sockaddr_un remote;
2041 unsigned int t = sizeof(remote);
2043 GIOChannel *clientchan;
2045 clientsock = accept (g_io_channel_unix_get_fd(chan),
2046 (struct sockaddr *) &remote, &t);
2048 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2049 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2050 (GIOFunc) control_client_socket, clientchan);
2057 control_client_socket(GIOChannel *clientchan) {
2059 GString *result = g_string_new("");
2060 GError *error = NULL;
2064 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2065 if (ret == G_IO_STATUS_ERROR) {
2066 g_warning ("Error reading: %s\n", error->message);
2067 g_io_channel_shutdown(clientchan, TRUE, &error);
2069 } else if (ret == G_IO_STATUS_EOF) {
2070 /* shutdown and remove channel watch from main loop */
2071 g_io_channel_shutdown(clientchan, TRUE, &error);
2076 parse_cmd_line (ctl_line, result);
2077 g_string_append_c(result, '\n');
2078 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2080 if (ret == G_IO_STATUS_ERROR) {
2081 g_warning ("Error writing: %s", error->message);
2083 g_io_channel_flush(clientchan, &error);
2086 if (error) g_error_free (error);
2087 g_string_free(result, TRUE);
2093 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2094 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2095 if (unlink(uzbl.comm.socket_path) == -1)
2096 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2097 g_free(uzbl.comm.socket_path);
2098 uzbl.comm.socket_path = NULL;
2106 GIOChannel *chan = NULL;
2108 struct sockaddr_un local;
2109 gchar *path = build_stream_name(SOCKET, dir);
2111 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2113 local.sun_family = AF_UNIX;
2114 strcpy (local.sun_path, path);
2115 unlink (local.sun_path);
2117 len = strlen (local.sun_path) + sizeof (local.sun_family);
2118 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2119 if (uzbl.state.verbose)
2120 printf ("init_socket: opened in %s\n", path);
2123 if( (chan = g_io_channel_unix_new(sock)) ) {
2124 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2125 uzbl.comm.socket_path = path;
2128 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2130 /* if we got this far, there was an error; cleanup */
2137 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2138 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2140 // this function may be called very early when the templates are not set (yet), hence the checks
2142 update_title (void) {
2143 Behaviour *b = &uzbl.behave;
2146 if (b->show_status) {
2147 if (b->title_format_short) {
2148 parsed = expand_template(b->title_format_short, FALSE);
2149 if (uzbl.gui.main_window)
2150 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2153 if (b->status_format) {
2154 parsed = expand_template(b->status_format, TRUE);
2155 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2158 if (b->status_background) {
2160 gdk_color_parse (b->status_background, &color);
2161 //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)
2162 if (uzbl.gui.main_window)
2163 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2164 else if (uzbl.gui.plug)
2165 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2168 if (b->title_format_long) {
2169 parsed = expand_template(b->title_format_long, FALSE);
2170 if (uzbl.gui.main_window)
2171 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2178 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2182 retreive_geometry();
2187 key_press_cb (GtkWidget* window, GdkEventKey* event)
2189 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2193 if (event->type != GDK_KEY_PRESS ||
2194 event->keyval == GDK_Page_Up ||
2195 event->keyval == GDK_Page_Down ||
2196 event->keyval == GDK_Up ||
2197 event->keyval == GDK_Down ||
2198 event->keyval == GDK_Left ||
2199 event->keyval == GDK_Right ||
2200 event->keyval == GDK_Shift_L ||
2201 event->keyval == GDK_Shift_R)
2204 /* turn off insert mode (if always_insert_mode is not used) */
2205 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2206 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2211 if (uzbl.behave.insert_mode &&
2212 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2213 (!uzbl.behave.modmask)
2218 if (event->keyval == GDK_Escape) {
2219 g_string_truncate(uzbl.state.keycmd, 0);
2221 dehilight(uzbl.gui.web_view, NULL, NULL);
2225 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2226 if (event->keyval == GDK_Insert) {
2228 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2229 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2231 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2234 g_string_append (uzbl.state.keycmd, str);
2241 if (event->keyval == GDK_BackSpace)
2242 keycmd_bs(NULL, NULL, NULL);
2244 gboolean key_ret = FALSE;
2245 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2247 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2249 run_keycmd(key_ret);
2251 if (key_ret) return (!uzbl.behave.insert_mode);
2256 run_keycmd(const gboolean key_ret) {
2257 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2259 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2260 g_string_truncate(uzbl.state.keycmd, 0);
2261 parse_command(act->name, act->param, NULL);
2265 /* try if it's an incremental keycmd or one that takes args, and run it */
2266 GString* short_keys = g_string_new ("");
2267 GString* short_keys_inc = g_string_new ("");
2269 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2270 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2271 g_string_assign(short_keys_inc, short_keys->str);
2272 g_string_append_c(short_keys, '_');
2273 g_string_append_c(short_keys_inc, '*');
2275 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2276 /* run normal cmds only if return was pressed */
2277 exec_paramcmd(act, i);
2278 g_string_truncate(uzbl.state.keycmd, 0);
2280 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2281 if (key_ret) /* just quit the incremental command on return */
2282 g_string_truncate(uzbl.state.keycmd, 0);
2283 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2287 g_string_truncate(short_keys, short_keys->len - 1);
2289 g_string_free (short_keys, TRUE);
2290 g_string_free (short_keys_inc, TRUE);
2294 exec_paramcmd(const Action *act, const guint i) {
2295 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2296 GString *actionname = g_string_new ("");
2297 GString *actionparam = g_string_new ("");
2298 g_string_erase (parampart, 0, i+1);
2300 g_string_printf (actionname, act->name, parampart->str);
2302 g_string_printf (actionparam, act->param, parampart->str);
2303 parse_command(actionname->str, actionparam->str, NULL);
2304 g_string_free(actionname, TRUE);
2305 g_string_free(actionparam, TRUE);
2306 g_string_free(parampart, TRUE);
2314 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2315 //main_window_ref = g_object_ref(scrolled_window);
2316 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
2318 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2319 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2321 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2322 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2323 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2324 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2325 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2326 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2327 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2328 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2329 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2330 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2331 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2333 return scrolled_window;
2340 g->mainbar = gtk_hbox_new (FALSE, 0);
2342 /* keep a reference to the bar so we can re-pack it at runtime*/
2343 //sbar_ref = g_object_ref(g->mainbar);
2345 g->mainbar_label = gtk_label_new ("");
2346 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2347 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2348 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2349 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2350 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2351 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2356 GtkWidget* create_window () {
2357 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2358 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2359 gtk_widget_set_name (window, "Uzbl browser");
2360 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2361 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2362 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2368 GtkPlug* create_plug () {
2369 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2370 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2371 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2378 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2380 If actname is one that calls an external command, this function will inject
2381 newargs in front of the user-provided args in that command line. They will
2382 come become after the body of the script (in sh) or after the name of
2383 the command to execute (in spawn).
2384 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2385 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2387 The return value consist of two strings: the action (sh, ...) and its args.
2389 If act is not one that calls an external command, then the given action merely
2392 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2393 gchar *actdup = g_strdup(actname);
2394 g_array_append_val(rets, actdup);
2396 if ((g_strcmp0(actname, "spawn") == 0) ||
2397 (g_strcmp0(actname, "sh") == 0) ||
2398 (g_strcmp0(actname, "sync_spawn") == 0) ||
2399 (g_strcmp0(actname, "sync_sh") == 0)) {
2401 GString *a = g_string_new("");
2402 gchar **spawnparts = split_quoted(origargs, FALSE);
2403 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2404 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2406 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2407 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2409 g_array_append_val(rets, a->str);
2410 g_string_free(a, FALSE);
2411 g_strfreev(spawnparts);
2413 gchar *origdup = g_strdup(origargs);
2414 g_array_append_val(rets, origdup);
2416 return (gchar**)g_array_free(rets, FALSE);
2420 run_handler (const gchar *act, const gchar *args) {
2421 /* Consider this code a temporary hack to make the handlers usable.
2422 In practice, all this splicing, injection, and reconstruction is
2423 inefficient, annoying and hard to manage. Potential pitfalls arise
2424 when the handler specific args 1) are not quoted (the handler
2425 callbacks should take care of this) 2) are quoted but interfere
2426 with the users' own quotation. A more ideal solution is
2427 to refactor parse_command so that it doesn't just take a string
2428 and execute it; rather than that, we should have a function which
2429 returns the argument vector parsed from the string. This vector
2430 could be modified (e.g. insert additional args into it) before
2431 passing it to the next function that actually executes it. Though
2432 it still isn't perfect for chain actions.. will reconsider & re-
2433 factor when I have the time. -duc */
2435 char **parts = g_strsplit(act, " ", 2);
2437 if (g_strcmp0(parts[0], "chain") == 0) {
2438 GString *newargs = g_string_new("");
2439 gchar **chainparts = split_quoted(parts[1], FALSE);
2441 /* for every argument in the chain, inject the handler args
2442 and make sure the new parts are wrapped in quotes */
2443 gchar **cp = chainparts;
2445 gchar *quotless = NULL;
2446 gchar **spliced_quotless = NULL; // sigh -_-;
2447 gchar **inpart = NULL;
2450 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2452 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2453 } else quotless = g_strdup(*cp);
2455 spliced_quotless = g_strsplit(quotless, " ", 2);
2456 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2457 g_strfreev(spliced_quotless);
2459 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2465 parse_command(parts[0], &(newargs->str[1]), NULL);
2466 g_string_free(newargs, TRUE);
2467 g_strfreev(chainparts);
2470 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2471 parse_command(inparts[0], inparts[1], NULL);
2479 add_binding (const gchar *key, const gchar *act) {
2480 char **parts = g_strsplit(act, " ", 2);
2487 if (uzbl.state.verbose)
2488 printf ("Binding %-10s : %s\n", key, act);
2489 action = new_action(parts[0], parts[1]);
2491 if (g_hash_table_remove (uzbl.bindings, key))
2492 g_warning ("Overwriting existing binding for \"%s\"", key);
2493 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2498 get_xdg_var (XDG_Var xdg) {
2499 const gchar* actual_value = getenv (xdg.environmental);
2500 const gchar* home = getenv ("HOME");
2501 gchar* return_value;
2503 if (! actual_value || strcmp (actual_value, "") == 0) {
2504 if (xdg.default_value) {
2505 return_value = str_replace ("~", home, xdg.default_value);
2507 return_value = NULL;
2510 return_value = str_replace("~", home, actual_value);
2513 return return_value;
2517 find_xdg_file (int xdg_type, char* filename) {
2518 /* xdg_type = 0 => config
2519 xdg_type = 1 => data
2520 xdg_type = 2 => cache*/
2522 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2523 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2526 gchar* temporary_string;
2530 if (! file_exists (temporary_file) && xdg_type != 2) {
2531 buf = get_xdg_var (XDG[3 + xdg_type]);
2532 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2535 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2536 g_free (temporary_file);
2537 temporary_file = g_strconcat (temporary_string, filename, NULL);
2541 //g_free (temporary_string); - segfaults.
2543 if (file_exists (temporary_file)) {
2544 return temporary_file;
2551 State *s = &uzbl.state;
2552 Network *n = &uzbl.net;
2554 for (i = 0; default_config[i].command != NULL; i++) {
2555 parse_cmd_line(default_config[i].command, NULL);
2558 if (g_strcmp0(s->config_file, "-") == 0) {
2559 s->config_file = NULL;
2563 else if (!s->config_file) {
2564 s->config_file = find_xdg_file (0, "/uzbl/config");
2567 if (s->config_file) {
2568 GArray* lines = read_file_by_line (s->config_file);
2572 while ((line = g_array_index(lines, gchar*, i))) {
2573 parse_cmd_line (line, NULL);
2577 g_array_free (lines, TRUE);
2579 if (uzbl.state.verbose)
2580 printf ("No configuration file loaded.\n");
2583 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2586 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2589 if (!uzbl.behave.cookie_handler)
2592 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2593 GString *s = g_string_new ("");
2594 SoupURI * soup_uri = soup_message_get_uri(msg);
2595 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2596 run_handler(uzbl.behave.cookie_handler, s->str);
2598 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2599 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2600 if ( p != NULL ) *p = '\0';
2601 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2603 if (uzbl.comm.sync_stdout)
2604 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2606 g_string_free(s, TRUE);
2610 save_cookies (SoupMessage *msg, gpointer user_data){
2614 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2615 cookie = soup_cookie_to_set_cookie_header(ck->data);
2616 SoupURI * soup_uri = soup_message_get_uri(msg);
2617 GString *s = g_string_new ("");
2618 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2619 run_handler(uzbl.behave.cookie_handler, s->str);
2621 g_string_free(s, TRUE);
2626 /* --- WEBINSPECTOR --- */
2628 hide_window_cb(GtkWidget *widget, gpointer data) {
2631 gtk_widget_hide(widget);
2634 static WebKitWebView*
2635 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2638 (void) web_inspector;
2639 GtkWidget* scrolled_window;
2640 GtkWidget* new_web_view;
2643 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2644 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2645 G_CALLBACK(hide_window_cb), NULL);
2647 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2648 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2649 gtk_widget_show(g->inspector_window);
2651 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2652 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2653 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2654 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2655 gtk_widget_show(scrolled_window);
2657 new_web_view = webkit_web_view_new();
2658 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2660 return WEBKIT_WEB_VIEW(new_web_view);
2664 inspector_show_window_cb (WebKitWebInspector* inspector){
2666 gtk_widget_show(uzbl.gui.inspector_window);
2670 /* TODO: Add variables and code to make use of these functions */
2672 inspector_close_window_cb (WebKitWebInspector* inspector){
2678 inspector_attach_window_cb (WebKitWebInspector* inspector){
2684 inspector_detach_window_cb (WebKitWebInspector* inspector){
2690 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2696 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2702 set_up_inspector() {
2704 WebKitWebSettings *settings = view_settings();
2705 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2707 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2708 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2709 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2710 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2711 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2712 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2713 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2715 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2719 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2721 uzbl_cmdprop *c = v;
2726 if(c->type == TYPE_STR)
2727 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2728 else if(c->type == TYPE_INT)
2729 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2733 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2737 printf("bind %s = %s %s\n", (char *)k ,
2738 (char *)a->name, a->param?(char *)a->param:"");
2742 dump_config() { //ADD "result" var so we can use this with uzblctrl
2743 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2744 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2748 retreive_geometry() {
2750 GString *buf = g_string_new("");
2752 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2753 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2755 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2757 if(uzbl.gui.geometry)
2758 g_free(uzbl.gui.geometry);
2759 uzbl.gui.geometry = g_string_free(buf, FALSE);
2764 main (int argc, char* argv[]) {
2765 gtk_init (&argc, &argv);
2766 if (!g_thread_supported ())
2767 g_thread_init (NULL);
2768 uzbl.state.executable_path = g_strdup(argv[0]);
2769 uzbl.state.selected_url = NULL;
2770 uzbl.state.searchtx = NULL;
2772 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2773 g_option_context_add_main_entries (context, entries, NULL);
2774 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2775 g_option_context_parse (context, &argc, &argv, NULL);
2776 g_option_context_free(context);
2778 if (uzbl.behave.print_version) {
2779 printf("Commit: %s\n", COMMIT);
2783 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2784 if (argc > 1 && !uzbl.state.uri)
2785 uri_override = g_strdup(argv[1]);
2786 gboolean verbose_override = uzbl.state.verbose;
2788 /* initialize hash table */
2789 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2791 uzbl.net.soup_session = webkit_get_default_session();
2792 uzbl.state.keycmd = g_string_new("");
2794 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2795 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2796 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2797 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2798 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2799 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2802 if(uname(&uzbl.state.unameinfo) == -1)
2803 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2805 uzbl.gui.sbar.progress_s = g_strdup("=");
2806 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2807 uzbl.gui.sbar.progress_w = 10;
2809 /* HTML mode defaults*/
2810 uzbl.behave.html_buffer = g_string_new("");
2811 uzbl.behave.html_endmarker = g_strdup(".");
2812 uzbl.behave.html_timeout = 60;
2813 uzbl.behave.base_url = g_strdup("http://invalid");
2815 /* default mode indicators */
2816 uzbl.behave.insert_indicator = g_strdup("I");
2817 uzbl.behave.cmd_indicator = g_strdup("C");
2821 make_var_to_name_hash();
2823 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2825 uzbl.gui.scrolled_win = create_browser();
2828 /* initial packing */
2829 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2830 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2832 if (uzbl.state.socket_id) {
2833 uzbl.gui.plug = create_plug ();
2834 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2835 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2837 uzbl.gui.main_window = create_window ();
2838 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2839 gtk_widget_show_all (uzbl.gui.main_window);
2840 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2843 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2845 if (uzbl.state.verbose) {
2846 printf("Uzbl start location: %s\n", argv[0]);
2847 if (uzbl.state.socket_id)
2848 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2850 printf("window_id %i\n",(int) uzbl.xwin);
2851 printf("pid %i\n", getpid ());
2852 printf("name: %s\n", uzbl.state.instance_name);
2855 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2856 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2857 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2858 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2859 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2861 if(uzbl.gui.geometry)
2864 retreive_geometry();
2868 if (!uzbl.behave.show_status)
2869 gtk_widget_hide(uzbl.gui.mainbar);
2876 if (verbose_override > uzbl.state.verbose)
2877 uzbl.state.verbose = verbose_override;
2880 set_var_value("uri", uri_override);
2881 g_free(uri_override);
2882 } else if (uzbl.state.uri)
2883 cmd_load_uri(uzbl.gui.web_view, NULL);
2888 return EXIT_SUCCESS;
2891 /* vi: set et ts=4: */