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 { NULL, 0, 0, 0, NULL, NULL, NULL }
83 /* associate command names to their properties */
84 typedef const struct {
91 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
93 /* an abbreviation to help keep the table's width humane */
94 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* --------------------------------------------------------------------------------------- */
102 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "geometry", PTR(uzbl.gui.geometry, STR, 1, cmd_set_geometry)},
104 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
105 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
106 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
107 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
108 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
109 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
110 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
111 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
112 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
113 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
114 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
115 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
116 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
117 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
118 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
119 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
120 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
121 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
122 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
123 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
124 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
125 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
126 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
127 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
128 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
129 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
130 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
131 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
132 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
133 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
134 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
135 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
136 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
137 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
138 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
139 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
140 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
141 /* exported WebKitWebSettings properties */
142 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
143 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
144 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
145 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
146 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
147 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
148 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
149 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
150 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
151 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
152 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
153 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
154 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
155 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
156 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
157 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
159 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
160 }, *n2v_p = var_name_to_ptr;
166 { "SHIFT", GDK_SHIFT_MASK }, // shift
167 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
168 { "CONTROL", GDK_CONTROL_MASK }, // control
169 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
170 { "MOD2", GDK_MOD2_MASK }, // 5th mod
171 { "MOD3", GDK_MOD3_MASK }, // 6th mod
172 { "MOD4", GDK_MOD4_MASK }, // 7th mod
173 { "MOD5", GDK_MOD5_MASK }, // 8th mod
174 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
175 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
176 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
177 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
178 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
179 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
180 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
181 { "META", GDK_META_MASK }, // meta (since 2.10)
186 /* construct a hash from the var_name_to_ptr array for quick access */
188 make_var_to_name_hash() {
189 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
191 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
196 /* --- UTILITY FUNCTIONS --- */
198 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
200 get_exp_type(gchar *s) {
204 else if(*(s+1) == '{')
205 return EXP_BRACED_VAR;
206 else if(*(s+1) == '<')
209 return EXP_SIMPLE_VAR;
215 * recurse == 1: don't expand '@(command)@'
216 * recurse == 2: don't expand '@<java script>@'
219 expand(char *s, guint recurse) {
223 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
228 gchar *cmd_stdout = NULL;
230 GString *buf = g_string_new("");
231 GString *js_ret = g_string_new("");
236 g_string_append_c(buf, *++s);
241 etype = get_exp_type(s);
246 if( (vend = strpbrk(s, end_simple_var)) ||
247 (vend = strchr(s, '\0')) ) {
248 strncpy(ret, s, vend-s);
254 if( (vend = strchr(s, upto)) ||
255 (vend = strchr(s, '\0')) ) {
256 strncpy(ret, s, vend-s);
262 strcpy(str_end, ")@");
264 if( (vend = strstr(s, str_end)) ||
265 (vend = strchr(s, '\0')) ) {
266 strncpy(ret, s, vend-s);
272 strcpy(str_end, ">@");
274 if( (vend = strstr(s, str_end)) ||
275 (vend = strchr(s, '\0')) ) {
276 strncpy(ret, s, vend-s);
282 if(etype == EXP_SIMPLE_VAR ||
283 etype == EXP_BRACED_VAR) {
284 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
285 if(c->type == TYPE_STR)
286 g_string_append(buf, (gchar *)*c->ptr);
287 else if(c->type == TYPE_INT) {
288 char *b = itos((int)*c->ptr);
289 g_string_append(buf, b);
293 if(etype == EXP_SIMPLE_VAR)
298 else if(recurse != 1 &&
300 mycmd = expand(ret, 1);
301 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
305 g_printerr("error on running command: %s\n", err->message);
308 else if (*cmd_stdout) {
309 g_string_append(buf, cmd_stdout);
314 else if(recurse != 2 &&
316 mycmd = expand(ret, 2);
317 eval_js(uzbl.gui.web_view, mycmd, js_ret);
321 g_string_append(buf, js_ret->str);
322 g_string_free(js_ret, TRUE);
323 js_ret = g_string_new("");
330 g_string_append_c(buf, *s);
335 g_string_free(js_ret, TRUE);
336 return g_string_free(buf, FALSE);
343 snprintf(tmp, sizeof(tmp), "%i", val);
344 return g_strdup(tmp);
348 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
351 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
354 str_replace (const char* search, const char* replace, const char* string) {
358 buf = g_strsplit (string, search, -1);
359 ret = g_strjoinv (replace, buf);
360 g_strfreev(buf); // somebody said this segfaults
366 read_file_by_line (gchar *path) {
367 GIOChannel *chan = NULL;
368 gchar *readbuf = NULL;
370 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
373 chan = g_io_channel_new_file(path, "r", NULL);
376 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
377 const gchar* val = g_strdup (readbuf);
378 g_array_append_val (lines, val);
383 g_io_channel_unref (chan);
385 fprintf(stderr, "File '%s' not be read.\n", path);
392 gchar* parseenv (char* string) {
393 extern char** environ;
394 gchar* tmpstr = NULL;
398 while (environ[i] != NULL) {
399 gchar** env = g_strsplit (environ[i], "=", 2);
400 gchar* envname = g_strconcat ("$", env[0], NULL);
402 if (g_strrstr (string, envname) != NULL) {
403 tmpstr = g_strdup(string);
405 string = str_replace(envname, env[1], tmpstr);
410 g_strfreev (env); // somebody said this breaks uzbl
418 setup_signal(int signr, sigfunc *shandler) {
419 struct sigaction nh, oh;
421 nh.sa_handler = shandler;
422 sigemptyset(&nh.sa_mask);
425 if(sigaction(signr, &nh, &oh) < 0)
433 if (uzbl.behave.fifo_dir)
434 unlink (uzbl.comm.fifo_path);
435 if (uzbl.behave.socket_dir)
436 unlink (uzbl.comm.socket_path);
438 g_free(uzbl.state.executable_path);
439 g_string_free(uzbl.state.keycmd, TRUE);
440 g_hash_table_destroy(uzbl.bindings);
441 g_hash_table_destroy(uzbl.behave.commands);
444 /* used for html_mode_timeout
445 * be sure to extend this function to use
446 * more timers if needed in other places
449 set_timeout(int seconds) {
451 memset(&t, 0, sizeof t);
453 t.it_value.tv_sec = seconds;
454 t.it_value.tv_usec = 0;
455 setitimer(ITIMER_REAL, &t, NULL);
458 /* --- SIGNAL HANDLER --- */
461 catch_sigterm(int s) {
467 catch_sigint(int s) {
477 set_var_value("mode", "0");
482 /* --- CALLBACKS --- */
485 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
488 (void) navigation_action;
489 (void) policy_decision;
491 const gchar* uri = webkit_network_request_get_uri (request);
492 if (uzbl.state.verbose)
493 printf("New window requested -> %s \n", uri);
494 new_window_load_uri(uri);
499 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
504 /* If we can display it, let's display it... */
505 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
506 webkit_web_policy_decision_use (policy_decision);
510 /* ...everything we can't displayed is downloaded */
511 webkit_web_policy_decision_download (policy_decision);
516 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
520 if (uzbl.state.selected_url != NULL) {
521 if (uzbl.state.verbose)
522 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
523 new_window_load_uri(uzbl.state.selected_url);
525 if (uzbl.state.verbose)
526 printf("New web view -> %s\n","Nothing to open, exiting");
532 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
535 if (uzbl.behave.download_handler) {
536 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
537 if (uzbl.state.verbose)
538 printf("Download -> %s\n",uri);
539 /* if urls not escaped, we may have to escape and quote uri before this call */
540 run_handler(uzbl.behave.download_handler, uri);
545 /* scroll a bar in a given direction */
547 scroll (GtkAdjustment* bar, GArray *argv) {
551 gdouble page_size = gtk_adjustment_get_page_size(bar);
552 gdouble value = gtk_adjustment_get_value(bar);
553 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
556 value += page_size * amount * 0.01;
560 max_value = gtk_adjustment_get_upper(bar) - page_size;
562 if (value > max_value)
563 value = max_value; /* don't scroll past the end of the page */
565 gtk_adjustment_set_value (bar, value);
569 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
570 (void) page; (void) argv; (void) result;
571 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
575 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
576 (void) page; (void) argv; (void) result;
577 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
578 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
582 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
583 (void) page; (void) result;
584 scroll(uzbl.gui.bar_v, argv);
588 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
589 (void) page; (void) result;
590 scroll(uzbl.gui.bar_h, argv);
595 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
596 if(uzbl.state.verbose)
597 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
603 if (!uzbl.behave.show_status) {
604 gtk_widget_hide(uzbl.gui.mainbar);
606 gtk_widget_show(uzbl.gui.mainbar);
612 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
617 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
621 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
626 if (uzbl.behave.show_status) {
627 gtk_widget_hide(uzbl.gui.mainbar);
629 gtk_widget_show(uzbl.gui.mainbar);
631 uzbl.behave.show_status = !uzbl.behave.show_status;
636 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
640 //Set selected_url state variable
641 g_free(uzbl.state.selected_url);
642 uzbl.state.selected_url = NULL;
644 uzbl.state.selected_url = g_strdup(link);
650 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
653 const gchar *title = webkit_web_view_get_title(web_view);
654 if (uzbl.gui.main_title)
655 g_free (uzbl.gui.main_title);
656 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
661 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
664 uzbl.gui.sbar.load_progress = progress;
669 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
673 if (uzbl.behave.load_finish_handler)
674 run_handler(uzbl.behave.load_finish_handler, "");
678 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
682 uzbl.gui.sbar.load_progress = 0;
683 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
684 if (uzbl.behave.load_start_handler)
685 run_handler(uzbl.behave.load_start_handler, "");
689 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
692 g_free (uzbl.state.uri);
693 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
694 uzbl.state.uri = g_string_free (newuri, FALSE);
695 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
696 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
699 if (uzbl.behave.load_commit_handler)
700 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
704 destroy_cb (GtkWidget* widget, gpointer data) {
712 if (uzbl.behave.history_handler) {
714 struct tm * timeinfo;
717 timeinfo = localtime ( &rawtime );
718 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
719 run_handler(uzbl.behave.history_handler, date);
724 /* VIEW funcs (little webkit wrappers) */
725 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
727 VIEWFUNC(reload_bypass_cache)
728 VIEWFUNC(stop_loading)
735 /* -- command to callback/function map for things we cannot attach to any signals */
736 static struct {char *key; CommandInfo value;} cmdlist[] =
737 { /* key function no_split */
738 { "back", {view_go_back, 0} },
739 { "forward", {view_go_forward, 0} },
740 { "scroll_vert", {scroll_vert, 0} },
741 { "scroll_horz", {scroll_horz, 0} },
742 { "scroll_begin", {scroll_begin, 0} },
743 { "scroll_end", {scroll_end, 0} },
744 { "reload", {view_reload, 0}, },
745 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
746 { "stop", {view_stop_loading, 0}, },
747 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
748 { "zoom_out", {view_zoom_out, 0}, },
749 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
750 { "uri", {load_uri, TRUE} },
751 { "js", {run_js, TRUE} },
752 { "script", {run_external_js, 0} },
753 { "toggle_status", {toggle_status_cb, 0} },
754 { "spawn", {spawn, 0} },
755 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
756 { "sh", {spawn_sh, 0} },
757 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
758 { "exit", {close_uzbl, 0} },
759 { "search", {search_forward_text, TRUE} },
760 { "search_reverse", {search_reverse_text, TRUE} },
761 { "dehilight", {dehilight, 0} },
762 { "toggle_insert_mode", {toggle_insert_mode, 0} },
763 { "set", {set_var, TRUE} },
764 //{ "get", {get_var, TRUE} },
765 { "bind", {act_bind, TRUE} },
766 { "dump_config", {act_dump_config, 0} },
767 { "keycmd", {keycmd, TRUE} },
768 { "keycmd_nl", {keycmd_nl, TRUE} },
769 { "keycmd_bs", {keycmd_bs, 0} },
770 { "chain", {chain, 0} },
771 { "print", {print, TRUE} }
778 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
780 for (i = 0; i < LENGTH(cmdlist); i++)
781 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
784 /* -- CORE FUNCTIONS -- */
787 free_action(gpointer act) {
788 Action *action = (Action*)act;
789 g_free(action->name);
791 g_free(action->param);
796 new_action(const gchar *name, const gchar *param) {
797 Action *action = g_new(Action, 1);
799 action->name = g_strdup(name);
801 action->param = g_strdup(param);
803 action->param = NULL;
809 file_exists (const char * filename) {
810 return (access(filename, F_OK) == 0);
814 set_var(WebKitWebView *page, GArray *argv, GString *result) {
815 (void) page; (void) result;
816 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
817 if (split[0] != NULL) {
818 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
819 set_var_value(g_strstrip(split[0]), value);
826 print(WebKitWebView *page, GArray *argv, GString *result) {
827 (void) page; (void) result;
830 buf = expand(argv_idx(argv, 0), 0);
831 g_string_assign(result, buf);
836 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
837 (void) page; (void) result;
838 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
839 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
840 add_binding(g_strstrip(split[0]), value);
852 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
853 (void) page; (void) result;
855 if (argv_idx(argv, 0)) {
856 if (strcmp (argv_idx(argv, 0), "0") == 0) {
857 uzbl.behave.insert_mode = FALSE;
859 uzbl.behave.insert_mode = TRUE;
862 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
869 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
872 if (argv_idx(argv, 0)) {
873 GString* newuri = g_string_new (argv_idx(argv, 0));
874 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
875 run_js(web_view, argv, NULL);
878 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
879 g_string_prepend (newuri, "http://");
880 /* if we do handle cookies, ask our handler for them */
881 webkit_web_view_load_uri (web_view, newuri->str);
882 g_string_free (newuri, TRUE);
890 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
891 size_t argumentCount, const JSValueRef arguments[],
892 JSValueRef* exception) {
897 JSStringRef js_result_string;
898 GString *result = g_string_new("");
900 if (argumentCount >= 1) {
901 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
902 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
903 char ctl_line[arg_size];
904 JSStringGetUTF8CString(arg, ctl_line, arg_size);
906 parse_cmd_line(ctl_line, result);
908 JSStringRelease(arg);
910 js_result_string = JSStringCreateWithUTF8CString(result->str);
912 g_string_free(result, TRUE);
914 return JSValueMakeString(ctx, js_result_string);
917 static JSStaticFunction js_static_functions[] = {
918 {"run", js_run_command, kJSPropertyAttributeNone},
923 /* This function creates the class and its definition, only once */
924 if (!uzbl.js.initialized) {
925 /* it would be pretty cool to make this dynamic */
926 uzbl.js.classdef = kJSClassDefinitionEmpty;
927 uzbl.js.classdef.staticFunctions = js_static_functions;
929 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
935 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
936 WebKitWebFrame *frame;
937 JSGlobalContextRef context;
938 JSObjectRef globalobject;
939 JSStringRef var_name;
941 JSStringRef js_script;
942 JSValueRef js_result;
943 JSStringRef js_result_string;
944 size_t js_result_size;
948 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
949 context = webkit_web_frame_get_global_context(frame);
950 globalobject = JSContextGetGlobalObject(context);
952 /* uzbl javascript namespace */
953 var_name = JSStringCreateWithUTF8CString("Uzbl");
954 JSObjectSetProperty(context, globalobject, var_name,
955 JSObjectMake(context, uzbl.js.classref, NULL),
956 kJSClassAttributeNone, NULL);
958 /* evaluate the script and get return value*/
959 js_script = JSStringCreateWithUTF8CString(script);
960 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
961 if (js_result && !JSValueIsUndefined(context, js_result)) {
962 js_result_string = JSValueToStringCopy(context, js_result, NULL);
963 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
965 if (js_result_size) {
966 char js_result_utf8[js_result_size];
967 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
968 g_string_assign(result, js_result_utf8);
971 JSStringRelease(js_result_string);
975 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
977 JSStringRelease(var_name);
978 JSStringRelease(js_script);
982 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
984 if (argv_idx(argv, 0))
985 eval_js(web_view, argv_idx(argv, 0), result);
989 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
991 if (argv_idx(argv, 0)) {
992 GArray* lines = read_file_by_line (argv_idx (argv, 0));
997 while ((line = g_array_index(lines, gchar*, i))) {
999 js = g_strdup (line);
1001 gchar* newjs = g_strconcat (js, line, NULL);
1008 if (uzbl.state.verbose)
1009 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1011 if (argv_idx (argv, 1)) {
1012 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1016 eval_js (web_view, js, result);
1018 g_array_free (lines, TRUE);
1023 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1024 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1025 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1026 webkit_web_view_unmark_text_matches (page);
1027 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1028 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1032 if (uzbl.state.searchtx) {
1033 if (uzbl.state.verbose)
1034 printf ("Searching: %s\n", uzbl.state.searchtx);
1035 webkit_web_view_set_highlight_text_matches (page, TRUE);
1036 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1041 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1043 search_text(page, argv, TRUE);
1047 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1049 search_text(page, argv, FALSE);
1053 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1054 (void) argv; (void) result;
1055 webkit_web_view_set_highlight_text_matches (page, FALSE);
1060 new_window_load_uri (const gchar * uri) {
1061 GString* to_execute = g_string_new ("");
1062 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1064 for (i = 0; entries[i].long_name != NULL; i++) {
1065 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1066 gchar** str = (gchar**)entries[i].arg_data;
1068 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1072 if (uzbl.state.verbose)
1073 printf("\n%s\n", to_execute->str);
1074 g_spawn_command_line_async (to_execute->str, NULL);
1075 g_string_free (to_execute, TRUE);
1079 chain (WebKitWebView *page, GArray *argv, GString *result) {
1080 (void) page; (void) result;
1082 gchar **parts = NULL;
1084 while ((a = argv_idx(argv, i++))) {
1085 parts = g_strsplit (a, " ", 2);
1086 parse_command(parts[0], parts[1], result);
1092 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1096 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1102 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1106 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1112 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1117 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1119 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1124 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1131 /* --Statusbar functions-- */
1133 build_progressbar_ascii(int percent) {
1134 int width=uzbl.gui.sbar.progress_w;
1137 GString *bar = g_string_new("");
1139 l = (double)percent*((double)width/100.);
1140 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1142 for(i=0; i<(int)l; i++)
1143 g_string_append(bar, uzbl.gui.sbar.progress_s);
1146 g_string_append(bar, uzbl.gui.sbar.progress_u);
1148 return g_string_free(bar, FALSE);
1153 const GScannerConfig scan_config = {
1156 ) /* cset_skip_characters */,
1161 ) /* cset_identifier_first */,
1168 ) /* cset_identifier_nth */,
1169 ( "" ) /* cpair_comment_single */,
1171 TRUE /* case_sensitive */,
1173 FALSE /* skip_comment_multi */,
1174 FALSE /* skip_comment_single */,
1175 FALSE /* scan_comment_multi */,
1176 TRUE /* scan_identifier */,
1177 TRUE /* scan_identifier_1char */,
1178 FALSE /* scan_identifier_NULL */,
1179 TRUE /* scan_symbols */,
1180 FALSE /* scan_binary */,
1181 FALSE /* scan_octal */,
1182 FALSE /* scan_float */,
1183 FALSE /* scan_hex */,
1184 FALSE /* scan_hex_dollar */,
1185 FALSE /* scan_string_sq */,
1186 FALSE /* scan_string_dq */,
1187 TRUE /* numbers_2_int */,
1188 FALSE /* int_2_float */,
1189 FALSE /* identifier_2_string */,
1190 FALSE /* char_2_token */,
1191 FALSE /* symbol_2_token */,
1192 TRUE /* scope_0_fallback */,
1197 uzbl.scan = g_scanner_new(&scan_config);
1198 while(symp->symbol_name) {
1199 g_scanner_scope_add_symbol(uzbl.scan, 0,
1201 GINT_TO_POINTER(symp->symbol_token));
1207 expand_template(const char *template, gboolean escape_markup) {
1208 if(!template) return NULL;
1210 GTokenType token = G_TOKEN_NONE;
1211 GString *ret = g_string_new("");
1215 g_scanner_input_text(uzbl.scan, template, strlen(template));
1216 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1217 token = g_scanner_get_next_token(uzbl.scan);
1219 if(token == G_TOKEN_SYMBOL) {
1220 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1224 buf = uzbl.state.uri?
1225 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1226 g_string_append(ret, buf);
1230 g_string_append(ret, uzbl.state.uri?
1231 uzbl.state.uri:g_strdup(""));
1234 buf = itos(uzbl.gui.sbar.load_progress);
1235 g_string_append(ret, buf);
1238 case SYM_LOADPRGSBAR:
1239 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1240 g_string_append(ret, buf);
1245 buf = uzbl.gui.main_title?
1246 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1247 g_string_append(ret, buf);
1251 g_string_append(ret, uzbl.gui.main_title?
1252 uzbl.gui.main_title:g_strdup(""));
1254 case SYM_SELECTED_URI:
1256 buf = uzbl.state.selected_url?
1257 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1258 g_string_append(ret, buf);
1262 g_string_append(ret, uzbl.state.selected_url?
1263 uzbl.state.selected_url:g_strdup(""));
1266 buf = itos(uzbl.xwin);
1267 g_string_append(ret,
1268 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1273 buf = uzbl.state.keycmd->str?
1274 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1275 g_string_append(ret, buf);
1279 g_string_append(ret, uzbl.state.keycmd->str?
1280 uzbl.state.keycmd->str:g_strdup(""));
1283 g_string_append(ret,
1284 uzbl.behave.insert_mode?
1285 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1288 g_string_append(ret,
1289 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1291 /* useragent syms */
1293 buf = itos(WEBKIT_MAJOR_VERSION);
1294 g_string_append(ret, buf);
1298 buf = itos(WEBKIT_MINOR_VERSION);
1299 g_string_append(ret, buf);
1303 buf = itos(WEBKIT_MICRO_VERSION);
1304 g_string_append(ret, buf);
1308 g_string_append(ret, uzbl.state.unameinfo.sysname);
1311 g_string_append(ret, uzbl.state.unameinfo.nodename);
1314 g_string_append(ret, uzbl.state.unameinfo.release);
1317 g_string_append(ret, uzbl.state.unameinfo.version);
1320 g_string_append(ret, uzbl.state.unameinfo.machine);
1323 g_string_append(ret, ARCH);
1326 case SYM_DOMAINNAME:
1327 g_string_append(ret, uzbl.state.unameinfo.domainname);
1331 g_string_append(ret, COMMIT);
1337 else if(token == G_TOKEN_INT) {
1338 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1339 g_string_append(ret, buf);
1342 else if(token == G_TOKEN_IDENTIFIER) {
1343 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1345 else if(token == G_TOKEN_CHAR) {
1346 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1350 return g_string_free(ret, FALSE);
1352 /* --End Statusbar functions-- */
1355 sharg_append(GArray *a, const gchar *str) {
1356 const gchar *s = (str ? str : "");
1357 g_array_append_val(a, s);
1360 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1362 run_command (const gchar *command, const guint npre, const gchar **args,
1363 const gboolean sync, char **output_stdout) {
1364 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1367 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1368 gchar *pid = itos(getpid());
1369 gchar *xwin = itos(uzbl.xwin);
1371 sharg_append(a, command);
1372 for (i = 0; i < npre; i++) /* add n args before the default vars */
1373 sharg_append(a, args[i]);
1374 sharg_append(a, uzbl.state.config_file);
1375 sharg_append(a, pid);
1376 sharg_append(a, xwin);
1377 sharg_append(a, uzbl.comm.fifo_path);
1378 sharg_append(a, uzbl.comm.socket_path);
1379 sharg_append(a, uzbl.state.uri);
1380 sharg_append(a, uzbl.gui.main_title);
1382 for (i = npre; i < g_strv_length((gchar**)args); i++)
1383 sharg_append(a, args[i]);
1387 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1389 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1390 NULL, NULL, output_stdout, NULL, NULL, &err);
1391 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1392 NULL, NULL, NULL, &err);
1394 if (uzbl.state.verbose) {
1395 GString *s = g_string_new("spawned:");
1396 for (i = 0; i < (a->len); i++) {
1397 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1398 g_string_append_printf(s, " %s", qarg);
1401 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1402 printf("%s\n", s->str);
1403 g_string_free(s, TRUE);
1405 printf("Stdout: %s\n", *output_stdout);
1409 g_printerr("error on run_command: %s\n", err->message);
1414 g_array_free (a, TRUE);
1419 split_quoted(const gchar* src, const gboolean unquote) {
1420 /* split on unquoted space, return array of strings;
1421 remove a layer of quotes and backslashes if unquote */
1422 if (!src) return NULL;
1424 gboolean dq = FALSE;
1425 gboolean sq = FALSE;
1426 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1427 GString *s = g_string_new ("");
1431 for (p = src; *p != '\0'; p++) {
1432 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1433 else if (*p == '\\') { g_string_append_c(s, *p++);
1434 g_string_append_c(s, *p); }
1435 else if ((*p == '"') && unquote && !sq) dq = !dq;
1436 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1438 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1439 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1441 else if ((*p == ' ') && !dq && !sq) {
1442 dup = g_strdup(s->str);
1443 g_array_append_val(a, dup);
1444 g_string_truncate(s, 0);
1445 } else g_string_append_c(s, *p);
1447 dup = g_strdup(s->str);
1448 g_array_append_val(a, dup);
1449 ret = (gchar**)a->data;
1450 g_array_free (a, FALSE);
1451 g_string_free (s, TRUE);
1456 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1457 (void)web_view; (void)result;
1458 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1459 if (argv_idx(argv, 0))
1460 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1464 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1465 (void)web_view; (void)result;
1467 if (argv_idx(argv, 0))
1468 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1469 TRUE, &uzbl.comm.sync_stdout);
1473 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1474 (void)web_view; (void)result;
1475 if (!uzbl.behave.shell_cmd) {
1476 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1481 gchar *spacer = g_strdup("");
1482 g_array_insert_val(argv, 1, spacer);
1483 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1485 for (i = 1; i < g_strv_length(cmd); i++)
1486 g_array_prepend_val(argv, cmd[i]);
1488 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1494 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1495 (void)web_view; (void)result;
1496 if (!uzbl.behave.shell_cmd) {
1497 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1502 gchar *spacer = g_strdup("");
1503 g_array_insert_val(argv, 1, spacer);
1504 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1506 for (i = 1; i < g_strv_length(cmd); i++)
1507 g_array_prepend_val(argv, cmd[i]);
1509 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1510 TRUE, &uzbl.comm.sync_stdout);
1516 parse_command(const char *cmd, const char *param, GString *result) {
1519 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1521 gchar **par = split_quoted(param, TRUE);
1522 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1524 if (c->no_split) { /* don't split */
1525 sharg_append(a, param);
1527 for (i = 0; i < g_strv_length(par); i++)
1528 sharg_append(a, par[i]);
1531 if (result == NULL) {
1532 GString *result_print = g_string_new("");
1534 c->function(uzbl.gui.web_view, a, result_print);
1535 if (result_print->len)
1536 printf("%*s\n", result_print->len, result_print->str);
1538 g_string_free(result_print, TRUE);
1540 c->function(uzbl.gui.web_view, a, result);
1543 g_array_free (a, TRUE);
1546 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1553 if(*uzbl.net.proxy_url == ' '
1554 || uzbl.net.proxy_url == NULL) {
1555 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1556 (GType) SOUP_SESSION_PROXY_URI);
1559 suri = soup_uri_new(uzbl.net.proxy_url);
1560 g_object_set(G_OBJECT(uzbl.net.soup_session),
1561 SOUP_SESSION_PROXY_URI,
1563 soup_uri_free(suri);
1570 if(file_exists(uzbl.gui.icon)) {
1571 if (uzbl.gui.main_window)
1572 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1574 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1576 g_free (uzbl.gui.icon);
1581 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1582 g_array_append_val (a, uzbl.state.uri);
1583 load_uri(uzbl.gui.web_view, a, NULL);
1584 g_array_free (a, TRUE);
1588 cmd_always_insert_mode() {
1589 uzbl.behave.insert_mode =
1590 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1596 g_object_set(G_OBJECT(uzbl.net.soup_session),
1597 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1601 cmd_max_conns_host() {
1602 g_object_set(G_OBJECT(uzbl.net.soup_session),
1603 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1608 soup_session_remove_feature
1609 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1610 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1611 /*g_free(uzbl.net.soup_logger);*/
1613 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1614 soup_session_add_feature(uzbl.net.soup_session,
1615 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1618 static WebKitWebSettings*
1620 return webkit_web_view_get_settings(uzbl.gui.web_view);
1625 WebKitWebSettings *ws = view_settings();
1626 if (uzbl.behave.font_size > 0) {
1627 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1630 if (uzbl.behave.monospace_size > 0) {
1631 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1632 uzbl.behave.monospace_size, NULL);
1634 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1635 uzbl.behave.font_size, NULL);
1641 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1645 cmd_disable_plugins() {
1646 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1647 !uzbl.behave.disable_plugins, NULL);
1651 cmd_disable_scripts() {
1652 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1653 !uzbl.behave.disable_scripts, NULL);
1657 cmd_minimum_font_size() {
1658 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1659 uzbl.behave.minimum_font_size, NULL);
1662 cmd_autoload_img() {
1663 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1664 uzbl.behave.autoload_img, NULL);
1669 cmd_autoshrink_img() {
1670 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1671 uzbl.behave.autoshrink_img, NULL);
1676 cmd_enable_spellcheck() {
1677 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1678 uzbl.behave.enable_spellcheck, NULL);
1682 cmd_enable_private() {
1683 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1684 uzbl.behave.enable_private, NULL);
1689 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1690 uzbl.behave.print_bg, NULL);
1695 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1696 uzbl.behave.style_uri, NULL);
1700 cmd_resizable_txt() {
1701 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1702 uzbl.behave.resizable_txt, NULL);
1706 cmd_default_encoding() {
1707 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1708 uzbl.behave.default_encoding, NULL);
1712 cmd_enforce_96dpi() {
1713 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1714 uzbl.behave.enforce_96dpi, NULL);
1718 cmd_caret_browsing() {
1719 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1720 uzbl.behave.caret_browsing, NULL);
1724 cmd_cookie_handler() {
1725 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1726 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1727 if ((g_strcmp0(split[0], "sh") == 0) ||
1728 (g_strcmp0(split[0], "spawn") == 0)) {
1729 g_free (uzbl.behave.cookie_handler);
1730 uzbl.behave.cookie_handler =
1731 g_strdup_printf("sync_%s %s", split[0], split[1]);
1738 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1743 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1748 if(uzbl.behave.inject_html) {
1749 webkit_web_view_load_html_string (uzbl.gui.web_view,
1750 uzbl.behave.inject_html, NULL);
1759 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1760 uzbl.behave.modmask = 0;
1762 if(uzbl.behave.modkey)
1763 g_free(uzbl.behave.modkey);
1764 uzbl.behave.modkey = buf;
1766 for (i = 0; modkeys[i].key != NULL; i++) {
1767 if (g_strrstr(buf, modkeys[i].key))
1768 uzbl.behave.modmask |= modkeys[i].mask;
1774 if (*uzbl.net.useragent == ' ') {
1775 g_free (uzbl.net.useragent);
1776 uzbl.net.useragent = NULL;
1778 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1780 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1781 g_free(uzbl.net.useragent);
1782 uzbl.net.useragent = ua;
1788 gtk_widget_ref(uzbl.gui.scrolled_win);
1789 gtk_widget_ref(uzbl.gui.mainbar);
1790 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1791 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1793 if(uzbl.behave.status_top) {
1794 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1795 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1798 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1799 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1801 gtk_widget_unref(uzbl.gui.scrolled_win);
1802 gtk_widget_unref(uzbl.gui.mainbar);
1803 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1808 set_var_value(gchar *name, gchar *val) {
1809 uzbl_cmdprop *c = NULL;
1813 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1814 /* check for the variable type */
1815 if (c->type == TYPE_STR) {
1816 buf = expand(val, 0);
1819 } else if(c->type == TYPE_INT) {
1820 int *ip = (int *)c->ptr;
1821 buf = expand(val, 0);
1822 *ip = (int)strtoul(buf, &endp, 10);
1824 } else if (c->type == TYPE_FLOAT) {
1825 float *fp = (float *)c->ptr;
1826 buf = expand(val, 0);
1827 *fp = strtod(buf, &endp);
1831 /* invoke a command specific function */
1832 if(c->func) c->func();
1839 Behaviour *b = &uzbl.behave;
1841 if(b->html_buffer->str) {
1842 webkit_web_view_load_html_string (uzbl.gui.web_view,
1843 b->html_buffer->str, b->base_url);
1844 g_string_free(b->html_buffer, TRUE);
1845 b->html_buffer = g_string_new("");
1849 enum {M_CMD, M_HTML};
1851 parse_cmd_line(const char *ctl_line, GString *result) {
1852 Behaviour *b = &uzbl.behave;
1855 if(b->mode == M_HTML) {
1856 len = strlen(b->html_endmarker);
1857 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1858 if(len == strlen(ctl_line)-1 &&
1859 !strncmp(b->html_endmarker, ctl_line, len)) {
1861 set_var_value("mode", "0");
1866 set_timeout(b->html_timeout);
1867 g_string_append(b->html_buffer, ctl_line);
1870 else if((ctl_line[0] == '#') /* Comments */
1871 || (ctl_line[0] == ' ')
1872 || (ctl_line[0] == '\n'))
1873 ; /* ignore these lines */
1874 else { /* parse a command */
1876 gchar **tokens = NULL;
1877 len = strlen(ctl_line);
1879 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1880 ctlstrip = g_strndup(ctl_line, len - 1);
1881 else ctlstrip = g_strdup(ctl_line);
1883 tokens = g_strsplit(ctlstrip, " ", 2);
1884 parse_command(tokens[0], tokens[1], result);
1891 build_stream_name(int type, const gchar* dir) {
1892 char *xwin_str = NULL;
1893 State *s = &uzbl.state;
1896 xwin_str = itos((int)uzbl.xwin);
1898 str = g_strdup_printf
1899 ("%s/uzbl_fifo_%s", dir,
1900 s->instance_name ? s->instance_name : xwin_str);
1901 } else if (type == SOCKET) {
1902 str = g_strdup_printf
1903 ("%s/uzbl_socket_%s", dir,
1904 s->instance_name ? s->instance_name : xwin_str );
1911 control_fifo(GIOChannel *gio, GIOCondition condition) {
1912 if (uzbl.state.verbose)
1913 printf("triggered\n");
1918 if (condition & G_IO_HUP)
1919 g_error ("Fifo: Read end of pipe died!\n");
1922 g_error ("Fifo: GIOChannel broke\n");
1924 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1925 if (ret == G_IO_STATUS_ERROR) {
1926 g_error ("Fifo: Error reading: %s\n", err->message);
1930 parse_cmd_line(ctl_line, NULL);
1937 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1938 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1939 if (unlink(uzbl.comm.fifo_path) == -1)
1940 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1941 g_free(uzbl.comm.fifo_path);
1942 uzbl.comm.fifo_path = NULL;
1945 if (*dir == ' ') { /* space unsets the variable */
1950 GIOChannel *chan = NULL;
1951 GError *error = NULL;
1952 gchar *path = build_stream_name(FIFO, dir);
1954 if (!file_exists(path)) {
1955 if (mkfifo (path, 0666) == 0) {
1956 // 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.
1957 chan = g_io_channel_new_file(path, "r+", &error);
1959 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1960 if (uzbl.state.verbose)
1961 printf ("init_fifo: created successfully as %s\n", path);
1962 uzbl.comm.fifo_path = path;
1964 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1965 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1966 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1967 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1969 /* if we got this far, there was an error; cleanup */
1970 if (error) g_error_free (error);
1977 control_stdin(GIOChannel *gio, GIOCondition condition) {
1979 gchar *ctl_line = NULL;
1982 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1983 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1986 parse_cmd_line(ctl_line, NULL);
1994 GIOChannel *chan = NULL;
1995 GError *error = NULL;
1997 chan = g_io_channel_unix_new(fileno(stdin));
1999 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2000 g_error ("Stdin: could not add watch\n");
2002 if (uzbl.state.verbose)
2003 printf ("Stdin: watch added successfully\n");
2006 g_error ("Stdin: Error while opening: %s\n", error->message);
2008 if (error) g_error_free (error);
2012 control_socket(GIOChannel *chan) {
2013 struct sockaddr_un remote;
2014 unsigned int t = sizeof(remote);
2016 GIOChannel *clientchan;
2018 clientsock = accept (g_io_channel_unix_get_fd(chan),
2019 (struct sockaddr *) &remote, &t);
2021 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2022 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2023 (GIOFunc) control_client_socket, clientchan);
2030 control_client_socket(GIOChannel *clientchan) {
2032 GString *result = g_string_new("");
2033 GError *error = NULL;
2037 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2038 if (ret == G_IO_STATUS_ERROR) {
2039 g_warning ("Error reading: %s\n", error->message);
2040 g_io_channel_shutdown(clientchan, TRUE, &error);
2042 } else if (ret == G_IO_STATUS_EOF) {
2043 /* shutdown and remove channel watch from main loop */
2044 g_io_channel_shutdown(clientchan, TRUE, &error);
2049 parse_cmd_line (ctl_line, result);
2050 g_string_append_c(result, '\n');
2051 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2053 if (ret == G_IO_STATUS_ERROR) {
2054 g_warning ("Error writing: %s", error->message);
2056 g_io_channel_flush(clientchan, &error);
2059 if (error) g_error_free (error);
2060 g_string_free(result, TRUE);
2066 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2067 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2068 if (unlink(uzbl.comm.socket_path) == -1)
2069 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2070 g_free(uzbl.comm.socket_path);
2071 uzbl.comm.socket_path = NULL;
2079 GIOChannel *chan = NULL;
2081 struct sockaddr_un local;
2082 gchar *path = build_stream_name(SOCKET, dir);
2084 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2086 local.sun_family = AF_UNIX;
2087 strcpy (local.sun_path, path);
2088 unlink (local.sun_path);
2090 len = strlen (local.sun_path) + sizeof (local.sun_family);
2091 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2092 if (uzbl.state.verbose)
2093 printf ("init_socket: opened in %s\n", path);
2096 if( (chan = g_io_channel_unix_new(sock)) ) {
2097 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2098 uzbl.comm.socket_path = path;
2101 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2103 /* if we got this far, there was an error; cleanup */
2110 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2111 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2113 // this function may be called very early when the templates are not set (yet), hence the checks
2115 update_title (void) {
2116 Behaviour *b = &uzbl.behave;
2119 if (b->show_status) {
2120 if (b->title_format_short) {
2121 parsed = expand_template(b->title_format_short, FALSE);
2122 if (uzbl.gui.main_window)
2123 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2126 if (b->status_format) {
2127 parsed = expand_template(b->status_format, TRUE);
2128 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2131 if (b->status_background) {
2133 gdk_color_parse (b->status_background, &color);
2134 //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2135 if (uzbl.gui.main_window)
2136 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2137 else if (uzbl.gui.plug)
2138 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2141 if (b->title_format_long) {
2142 parsed = expand_template(b->title_format_long, FALSE);
2143 if (uzbl.gui.main_window)
2144 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2151 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2155 retreive_geometry();
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);
2318 GtkWidget* create_window () {
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);
2324 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2330 GtkPlug* create_plug () {
2331 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2332 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2333 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2340 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2342 If actname is one that calls an external command, this function will inject
2343 newargs in front of the user-provided args in that command line. They will
2344 come become after the body of the script (in sh) or after the name of
2345 the command to execute (in spawn).
2346 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2347 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2349 The return value consist of two strings: the action (sh, ...) and its args.
2351 If act is not one that calls an external command, then the given action merely
2354 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2355 gchar *actdup = g_strdup(actname);
2356 g_array_append_val(rets, actdup);
2358 if ((g_strcmp0(actname, "spawn") == 0) ||
2359 (g_strcmp0(actname, "sh") == 0) ||
2360 (g_strcmp0(actname, "sync_spawn") == 0) ||
2361 (g_strcmp0(actname, "sync_sh") == 0)) {
2363 GString *a = g_string_new("");
2364 gchar **spawnparts = split_quoted(origargs, FALSE);
2365 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2366 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2368 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2369 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2371 g_array_append_val(rets, a->str);
2372 g_string_free(a, FALSE);
2373 g_strfreev(spawnparts);
2375 gchar *origdup = g_strdup(origargs);
2376 g_array_append_val(rets, origdup);
2378 return (gchar**)g_array_free(rets, FALSE);
2382 run_handler (const gchar *act, const gchar *args) {
2383 /* Consider this code a temporary hack to make the handlers usable.
2384 In practice, all this splicing, injection, and reconstruction is
2385 inefficient, annoying and hard to manage. Potential pitfalls arise
2386 when the handler specific args 1) are not quoted (the handler
2387 callbacks should take care of this) 2) are quoted but interfere
2388 with the users' own quotation. A more ideal solution is
2389 to refactor parse_command so that it doesn't just take a string
2390 and execute it; rather than that, we should have a function which
2391 returns the argument vector parsed from the string. This vector
2392 could be modified (e.g. insert additional args into it) before
2393 passing it to the next function that actually executes it. Though
2394 it still isn't perfect for chain actions.. will reconsider & re-
2395 factor when I have the time. -duc */
2397 char **parts = g_strsplit(act, " ", 2);
2399 if (g_strcmp0(parts[0], "chain") == 0) {
2400 GString *newargs = g_string_new("");
2401 gchar **chainparts = split_quoted(parts[1], FALSE);
2403 /* for every argument in the chain, inject the handler args
2404 and make sure the new parts are wrapped in quotes */
2405 gchar **cp = chainparts;
2407 gchar *quotless = NULL;
2408 gchar **spliced_quotless = NULL; // sigh -_-;
2409 gchar **inpart = NULL;
2412 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2414 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2415 } else quotless = g_strdup(*cp);
2417 spliced_quotless = g_strsplit(quotless, " ", 2);
2418 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2419 g_strfreev(spliced_quotless);
2421 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2427 parse_command(parts[0], &(newargs->str[1]), NULL);
2428 g_string_free(newargs, TRUE);
2429 g_strfreev(chainparts);
2432 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2433 parse_command(inparts[0], inparts[1], NULL);
2441 add_binding (const gchar *key, const gchar *act) {
2442 char **parts = g_strsplit(act, " ", 2);
2449 if (uzbl.state.verbose)
2450 printf ("Binding %-10s : %s\n", key, act);
2451 action = new_action(parts[0], parts[1]);
2453 if (g_hash_table_remove (uzbl.bindings, key))
2454 g_warning ("Overwriting existing binding for \"%s\"", key);
2455 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2460 get_xdg_var (XDG_Var xdg) {
2461 const gchar* actual_value = getenv (xdg.environmental);
2462 const gchar* home = getenv ("HOME");
2463 gchar* return_value;
2465 if (! actual_value || strcmp (actual_value, "") == 0) {
2466 if (xdg.default_value) {
2467 return_value = str_replace ("~", home, xdg.default_value);
2469 return_value = NULL;
2472 return_value = str_replace("~", home, actual_value);
2475 return return_value;
2479 find_xdg_file (int xdg_type, char* filename) {
2480 /* xdg_type = 0 => config
2481 xdg_type = 1 => data
2482 xdg_type = 2 => cache*/
2484 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2485 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2488 gchar* temporary_string;
2492 if (! file_exists (temporary_file) && xdg_type != 2) {
2493 buf = get_xdg_var (XDG[3 + xdg_type]);
2494 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2497 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2498 g_free (temporary_file);
2499 temporary_file = g_strconcat (temporary_string, filename, NULL);
2503 //g_free (temporary_string); - segfaults.
2505 if (file_exists (temporary_file)) {
2506 return temporary_file;
2513 State *s = &uzbl.state;
2514 Network *n = &uzbl.net;
2516 for (i = 0; default_config[i].command != NULL; i++) {
2517 parse_cmd_line(default_config[i].command, NULL);
2520 if (g_strcmp0(s->config_file, "-") == 0) {
2521 s->config_file = NULL;
2525 else if (!s->config_file) {
2526 s->config_file = find_xdg_file (0, "/uzbl/config");
2529 if (s->config_file) {
2530 GArray* lines = read_file_by_line (s->config_file);
2534 while ((line = g_array_index(lines, gchar*, i))) {
2535 parse_cmd_line (line, NULL);
2539 g_array_free (lines, TRUE);
2541 if (uzbl.state.verbose)
2542 printf ("No configuration file loaded.\n");
2545 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2548 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2551 if (!uzbl.behave.cookie_handler)
2554 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2555 GString *s = g_string_new ("");
2556 SoupURI * soup_uri = soup_message_get_uri(msg);
2557 g_string_printf(s, "GET '%s' '%s'", 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'", soup_uri->host, soup_uri->path, cookie);
2581 run_handler(uzbl.behave.cookie_handler, s->str);
2583 g_string_free(s, TRUE);
2588 /* --- WEBINSPECTOR --- */
2590 hide_window_cb(GtkWidget *widget, gpointer data) {
2593 gtk_widget_hide(widget);
2596 static WebKitWebView*
2597 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2600 (void) web_inspector;
2601 GtkWidget* scrolled_window;
2602 GtkWidget* new_web_view;
2605 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2606 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2607 G_CALLBACK(hide_window_cb), NULL);
2609 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2610 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2611 gtk_widget_show(g->inspector_window);
2613 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2614 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2615 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2616 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2617 gtk_widget_show(scrolled_window);
2619 new_web_view = webkit_web_view_new();
2620 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2622 return WEBKIT_WEB_VIEW(new_web_view);
2626 inspector_show_window_cb (WebKitWebInspector* inspector){
2628 gtk_widget_show(uzbl.gui.inspector_window);
2632 /* TODO: Add variables and code to make use of these functions */
2634 inspector_close_window_cb (WebKitWebInspector* inspector){
2640 inspector_attach_window_cb (WebKitWebInspector* inspector){
2646 inspector_detach_window_cb (WebKitWebInspector* inspector){
2652 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2658 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2664 set_up_inspector() {
2666 WebKitWebSettings *settings = view_settings();
2667 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2669 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2670 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2671 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2672 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2673 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2674 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2675 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2677 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2681 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2683 uzbl_cmdprop *c = v;
2688 if(c->type == TYPE_STR)
2689 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2690 else if(c->type == TYPE_INT)
2691 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2695 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2699 printf("bind %s = %s %s\n", (char *)k ,
2700 (char *)a->name, a->param?(char *)a->param:"");
2705 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2706 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2710 retreive_geometry() {
2712 GString *buf = g_string_new("");
2714 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2715 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2717 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2718 uzbl.gui.geometry = g_string_free(buf, FALSE);
2723 main (int argc, char* argv[]) {
2724 gtk_init (&argc, &argv);
2725 if (!g_thread_supported ())
2726 g_thread_init (NULL);
2727 uzbl.state.executable_path = g_strdup(argv[0]);
2728 uzbl.state.selected_url = NULL;
2729 uzbl.state.searchtx = NULL;
2731 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2732 g_option_context_add_main_entries (context, entries, NULL);
2733 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2734 g_option_context_parse (context, &argc, &argv, NULL);
2735 g_option_context_free(context);
2737 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2738 gboolean verbose_override = uzbl.state.verbose;
2740 /* initialize hash table */
2741 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2743 uzbl.net.soup_session = webkit_get_default_session();
2744 uzbl.state.keycmd = g_string_new("");
2746 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2747 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2748 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2749 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2750 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2751 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2754 if(uname(&uzbl.state.unameinfo) == -1)
2755 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2757 uzbl.gui.sbar.progress_s = g_strdup("=");
2758 uzbl.gui.sbar.progress_u = g_strdup("·");
2759 uzbl.gui.sbar.progress_w = 10;
2761 /* HTML mode defaults*/
2762 uzbl.behave.html_buffer = g_string_new("");
2763 uzbl.behave.html_endmarker = g_strdup(".");
2764 uzbl.behave.html_timeout = 60;
2765 uzbl.behave.base_url = g_strdup("http://invalid");
2767 /* default mode indicators */
2768 uzbl.behave.insert_indicator = g_strdup("I");
2769 uzbl.behave.cmd_indicator = g_strdup("C");
2773 make_var_to_name_hash();
2775 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2777 uzbl.gui.scrolled_win = create_browser();
2780 /* initial packing */
2781 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2782 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2784 if (uzbl.state.socket_id) {
2785 uzbl.gui.plug = create_plug ();
2786 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2787 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2789 uzbl.gui.main_window = create_window ();
2790 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2791 gtk_widget_show_all (uzbl.gui.main_window);
2792 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2795 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2797 if (uzbl.state.verbose) {
2798 printf("Uzbl start location: %s\n", argv[0]);
2799 if (uzbl.state.socket_id)
2800 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2802 printf("window_id %i\n",(int) uzbl.xwin);
2803 printf("pid %i\n", getpid ());
2804 printf("name: %s\n", uzbl.state.instance_name);
2807 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2808 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2809 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2810 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2811 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2813 if(uzbl.gui.geometry)
2816 retreive_geometry();
2820 if (!uzbl.behave.show_status)
2821 gtk_widget_hide(uzbl.gui.mainbar);
2828 if (verbose_override > uzbl.state.verbose)
2829 uzbl.state.verbose = verbose_override;
2832 set_var_value("uri", uri_override);
2833 g_free(uri_override);
2834 } else if (uzbl.state.uri)
2835 cmd_load_uri(uzbl.gui.web_view, NULL);
2840 return EXIT_SUCCESS;
2843 /* vi: set et ts=4: */