1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { NULL, 0, 0, 0, NULL, NULL, NULL }
79 /* associate command names to their properties */
80 typedef const struct {
81 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
82 the PTR() macro is kind of preventing this change at the moment. */
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
163 { "SHIFT", GDK_SHIFT_MASK }, // shift
164 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
165 { "CONTROL", GDK_CONTROL_MASK }, // control
166 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
167 { "MOD2", GDK_MOD2_MASK }, // 5th mod
168 { "MOD3", GDK_MOD3_MASK }, // 6th mod
169 { "MOD4", GDK_MOD4_MASK }, // 7th mod
170 { "MOD5", GDK_MOD5_MASK }, // 8th mod
171 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
177 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
178 { "META", GDK_META_MASK }, // meta (since 2.10)
183 /* construct a hash from the var_name_to_ptr array for quick access */
185 make_var_to_name_hash() {
186 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
188 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
193 /* --- UTILITY FUNCTIONS --- */
194 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
196 get_exp_type(gchar *s) {
200 else if(*(s+1) == '{')
201 return EXP_BRACED_VAR;
202 else if(*(s+1) == '<')
205 return EXP_SIMPLE_VAR;
211 * recurse == 1: don't expand '@(command)@'
212 * recurse == 2: don't expand '@<java script>@'
215 expand(char *s, guint recurse) {
219 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
224 gchar *cmd_stdout = NULL;
226 GString *buf = g_string_new("");
227 GString *js_ret = g_string_new("");
232 g_string_append_c(buf, *++s);
237 etype = get_exp_type(s);
242 if( (vend = strpbrk(s, end_simple_var)) ||
243 (vend = strchr(s, '\0')) ) {
244 strncpy(ret, s, vend-s);
250 if( (vend = strchr(s, upto)) ||
251 (vend = strchr(s, '\0')) ) {
252 strncpy(ret, s, vend-s);
258 strcpy(str_end, ")@");
260 if( (vend = strstr(s, str_end)) ||
261 (vend = strchr(s, '\0')) ) {
262 strncpy(ret, s, vend-s);
268 strcpy(str_end, ">@");
270 if( (vend = strstr(s, str_end)) ||
271 (vend = strchr(s, '\0')) ) {
272 strncpy(ret, s, vend-s);
278 if(etype == EXP_SIMPLE_VAR ||
279 etype == EXP_BRACED_VAR) {
280 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
281 if(c->type == TYPE_STR)
282 g_string_append(buf, (gchar *)*c->ptr);
283 else if(c->type == TYPE_INT) {
284 g_string_append_printf(buf, "%d", (int)*c->ptr);
286 else if(c->type == TYPE_FLOAT) {
287 g_string_append_printf(buf, "%f", *(float *)c->ptr);
290 if(etype == EXP_SIMPLE_VAR)
295 else if(recurse != 1 &&
297 mycmd = expand(ret, 1);
298 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
302 g_printerr("error on running command: %s\n", err->message);
305 else if (*cmd_stdout) {
306 g_string_append(buf, cmd_stdout);
311 else if(recurse != 2 &&
313 mycmd = expand(ret, 2);
314 eval_js(uzbl.gui.web_view, mycmd, js_ret);
318 g_string_append(buf, js_ret->str);
319 g_string_free(js_ret, TRUE);
320 js_ret = g_string_new("");
327 g_string_append_c(buf, *s);
332 g_string_free(js_ret, TRUE);
333 return g_string_free(buf, FALSE);
340 snprintf(tmp, sizeof(tmp), "%i", val);
341 return g_strdup(tmp);
345 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
348 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
351 str_replace (const char* search, const char* replace, const char* string) {
355 buf = g_strsplit (string, search, -1);
356 ret = g_strjoinv (replace, buf);
357 g_strfreev(buf); // somebody said this segfaults
363 read_file_by_line (gchar *path) {
364 GIOChannel *chan = NULL;
365 gchar *readbuf = NULL;
367 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
370 chan = g_io_channel_new_file(path, "r", NULL);
373 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
374 const gchar* val = g_strdup (readbuf);
375 g_array_append_val (lines, val);
380 g_io_channel_unref (chan);
382 fprintf(stderr, "File '%s' not be read.\n", path);
389 parseenv (char* string) {
390 extern char** environ;
391 gchar* tmpstr = NULL;
395 while (environ[i] != NULL) {
396 gchar** env = g_strsplit (environ[i], "=", 2);
397 gchar* envname = g_strconcat ("$", env[0], NULL);
399 if (g_strrstr (string, envname) != NULL) {
400 tmpstr = g_strdup(string);
402 string = str_replace(envname, env[1], tmpstr);
407 g_strfreev (env); // somebody said this breaks uzbl
415 setup_signal(int signr, sigfunc *shandler) {
416 struct sigaction nh, oh;
418 nh.sa_handler = shandler;
419 sigemptyset(&nh.sa_mask);
422 if(sigaction(signr, &nh, &oh) < 0)
430 if (uzbl.behave.fifo_dir)
431 unlink (uzbl.comm.fifo_path);
432 if (uzbl.behave.socket_dir)
433 unlink (uzbl.comm.socket_path);
435 g_free(uzbl.state.executable_path);
436 g_string_free(uzbl.state.keycmd, TRUE);
437 g_hash_table_destroy(uzbl.bindings);
438 g_hash_table_destroy(uzbl.behave.commands);
439 g_scanner_destroy(uzbl.scan);
442 /* used for html_mode_timeout
443 * be sure to extend this function to use
444 * more timers if needed in other places
447 set_timeout(int seconds) {
449 memset(&t, 0, sizeof t);
451 t.it_value.tv_sec = seconds;
452 t.it_value.tv_usec = 0;
453 setitimer(ITIMER_REAL, &t, NULL);
456 /* --- SIGNAL HANDLER --- */
459 catch_sigterm(int s) {
465 catch_sigint(int s) {
475 set_var_value("mode", "0");
480 /* --- CALLBACKS --- */
483 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
486 (void) navigation_action;
487 (void) policy_decision;
489 const gchar* uri = webkit_network_request_get_uri (request);
490 if (uzbl.state.verbose)
491 printf("New window requested -> %s \n", uri);
492 new_window_load_uri(uri);
497 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
502 /* If we can display it, let's display it... */
503 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
504 webkit_web_policy_decision_use (policy_decision);
508 /* ...everything we can't displayed is downloaded */
509 webkit_web_policy_decision_download (policy_decision);
514 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
518 if (uzbl.state.selected_url != NULL) {
519 if (uzbl.state.verbose)
520 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
521 new_window_load_uri(uzbl.state.selected_url);
523 if (uzbl.state.verbose)
524 printf("New web view -> %s\n","Nothing to open, exiting");
530 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
533 if (uzbl.behave.download_handler) {
534 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
535 if (uzbl.state.verbose)
536 printf("Download -> %s\n",uri);
537 /* if urls not escaped, we may have to escape and quote uri before this call */
538 run_handler(uzbl.behave.download_handler, uri);
543 /* scroll a bar in a given direction */
545 scroll (GtkAdjustment* bar, GArray *argv) {
549 gdouble page_size = gtk_adjustment_get_page_size(bar);
550 gdouble value = gtk_adjustment_get_value(bar);
551 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
554 value += page_size * amount * 0.01;
558 max_value = gtk_adjustment_get_upper(bar) - page_size;
560 if (value > max_value)
561 value = max_value; /* don't scroll past the end of the page */
563 gtk_adjustment_set_value (bar, value);
567 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
568 (void) page; (void) argv; (void) result;
569 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
573 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
574 (void) page; (void) argv; (void) result;
575 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
576 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
580 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
581 (void) page; (void) result;
582 scroll(uzbl.gui.bar_v, argv);
586 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
587 (void) page; (void) result;
588 scroll(uzbl.gui.bar_h, argv);
593 if (!uzbl.behave.show_status) {
594 gtk_widget_hide(uzbl.gui.mainbar);
596 gtk_widget_show(uzbl.gui.mainbar);
602 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
607 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
611 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
616 if (uzbl.behave.show_status) {
617 gtk_widget_hide(uzbl.gui.mainbar);
619 gtk_widget_show(uzbl.gui.mainbar);
621 uzbl.behave.show_status = !uzbl.behave.show_status;
626 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
630 //Set selected_url state variable
631 g_free(uzbl.state.selected_url);
632 uzbl.state.selected_url = NULL;
634 uzbl.state.selected_url = g_strdup(link);
640 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
643 const gchar *title = webkit_web_view_get_title(web_view);
644 if (uzbl.gui.main_title)
645 g_free (uzbl.gui.main_title);
646 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
651 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
654 uzbl.gui.sbar.load_progress = progress;
659 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
663 if (uzbl.behave.load_finish_handler)
664 run_handler(uzbl.behave.load_finish_handler, "");
668 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
672 uzbl.gui.sbar.load_progress = 0;
673 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
674 if (uzbl.behave.load_start_handler)
675 run_handler(uzbl.behave.load_start_handler, "");
679 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
682 g_free (uzbl.state.uri);
683 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
684 uzbl.state.uri = g_string_free (newuri, FALSE);
685 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
686 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
689 if (uzbl.behave.load_commit_handler)
690 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
694 destroy_cb (GtkWidget* widget, gpointer data) {
702 if (uzbl.behave.history_handler) {
706 g_get_current_time(&the_time);
707 /* no need to wrap this string with quotes since it contains no spaces.
708 format is like: 2009-06-26T20:02:05.262864Z */
709 date = g_time_val_to_iso8601(&the_time);
710 run_handler(uzbl.behave.history_handler, date);
716 /* VIEW funcs (little webkit wrappers) */
717 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
719 VIEWFUNC(reload_bypass_cache)
720 VIEWFUNC(stop_loading)
727 /* -- command to callback/function map for things we cannot attach to any signals */
728 struct {char *key; CommandInfo value;} cmdlist[] =
729 { /* key function no_split */
730 { "back", {view_go_back, 0} },
731 { "forward", {view_go_forward, 0} },
732 { "scroll_vert", {scroll_vert, 0} },
733 { "scroll_horz", {scroll_horz, 0} },
734 { "scroll_begin", {scroll_begin, 0} },
735 { "scroll_end", {scroll_end, 0} },
736 { "reload", {view_reload, 0}, },
737 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
738 { "stop", {view_stop_loading, 0}, },
739 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
740 { "zoom_out", {view_zoom_out, 0}, },
741 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
742 { "uri", {load_uri, TRUE} },
743 { "js", {run_js, TRUE} },
744 { "script", {run_external_js, 0} },
745 { "toggle_status", {toggle_status_cb, 0} },
746 { "spawn", {spawn, 0} },
747 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
748 { "sh", {spawn_sh, 0} },
749 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
750 { "exit", {close_uzbl, 0} },
751 { "quit", {close_uzbl, 0} },
752 { "search", {search_forward_text, TRUE} },
753 { "search_reverse", {search_reverse_text, TRUE} },
754 { "dehilight", {dehilight, 0} },
755 { "toggle_insert_mode", {toggle_insert_mode, 0} },
756 { "set", {set_var, TRUE} },
757 //{ "get", {get_var, TRUE} },
758 { "bind", {act_bind, TRUE} },
759 { "dump_config", {act_dump_config, 0} },
760 { "keycmd", {keycmd, TRUE} },
761 { "keycmd_nl", {keycmd_nl, TRUE} },
762 { "keycmd_bs", {keycmd_bs, 0} },
763 { "chain", {chain, 0} },
764 { "print", {print, TRUE} }
771 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
773 for (i = 0; i < LENGTH(cmdlist); i++)
774 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
777 /* -- CORE FUNCTIONS -- */
780 free_action(gpointer act) {
781 Action *action = (Action*)act;
782 g_free(action->name);
784 g_free(action->param);
789 new_action(const gchar *name, const gchar *param) {
790 Action *action = g_new(Action, 1);
792 action->name = g_strdup(name);
794 action->param = g_strdup(param);
796 action->param = NULL;
802 file_exists (const char * filename) {
803 return (access(filename, F_OK) == 0);
807 set_var(WebKitWebView *page, GArray *argv, GString *result) {
808 (void) page; (void) result;
809 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
810 if (split[0] != NULL) {
811 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
812 set_var_value(g_strstrip(split[0]), value);
819 print(WebKitWebView *page, GArray *argv, GString *result) {
820 (void) page; (void) result;
823 buf = expand(argv_idx(argv, 0), 0);
824 g_string_assign(result, buf);
829 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
830 (void) page; (void) result;
831 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
832 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
833 add_binding(g_strstrip(split[0]), value);
845 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
846 (void) page; (void) result;
848 if (argv_idx(argv, 0)) {
849 if (strcmp (argv_idx(argv, 0), "0") == 0) {
850 uzbl.behave.insert_mode = FALSE;
852 uzbl.behave.insert_mode = TRUE;
855 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
862 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
865 if (argv_idx(argv, 0)) {
866 GString* newuri = g_string_new (argv_idx(argv, 0));
867 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
868 run_js(web_view, argv, NULL);
871 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
872 g_string_prepend (newuri, "http://");
873 /* if we do handle cookies, ask our handler for them */
874 webkit_web_view_load_uri (web_view, newuri->str);
875 g_string_free (newuri, TRUE);
882 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
883 size_t argumentCount, const JSValueRef arguments[],
884 JSValueRef* exception) {
889 JSStringRef js_result_string;
890 GString *result = g_string_new("");
892 if (argumentCount >= 1) {
893 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
894 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
895 char ctl_line[arg_size];
896 JSStringGetUTF8CString(arg, ctl_line, arg_size);
898 parse_cmd_line(ctl_line, result);
900 JSStringRelease(arg);
902 js_result_string = JSStringCreateWithUTF8CString(result->str);
904 g_string_free(result, TRUE);
906 return JSValueMakeString(ctx, js_result_string);
909 JSStaticFunction js_static_functions[] = {
910 {"run", js_run_command, kJSPropertyAttributeNone},
915 /* This function creates the class and its definition, only once */
916 if (!uzbl.js.initialized) {
917 /* it would be pretty cool to make this dynamic */
918 uzbl.js.classdef = kJSClassDefinitionEmpty;
919 uzbl.js.classdef.staticFunctions = js_static_functions;
921 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
927 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
928 WebKitWebFrame *frame;
929 JSGlobalContextRef context;
930 JSObjectRef globalobject;
931 JSStringRef var_name;
933 JSStringRef js_script;
934 JSValueRef js_result;
935 JSStringRef js_result_string;
936 size_t js_result_size;
940 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
941 context = webkit_web_frame_get_global_context(frame);
942 globalobject = JSContextGetGlobalObject(context);
944 /* uzbl javascript namespace */
945 var_name = JSStringCreateWithUTF8CString("Uzbl");
946 JSObjectSetProperty(context, globalobject, var_name,
947 JSObjectMake(context, uzbl.js.classref, NULL),
948 kJSClassAttributeNone, NULL);
950 /* evaluate the script and get return value*/
951 js_script = JSStringCreateWithUTF8CString(script);
952 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
953 if (js_result && !JSValueIsUndefined(context, js_result)) {
954 js_result_string = JSValueToStringCopy(context, js_result, NULL);
955 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
957 if (js_result_size) {
958 char js_result_utf8[js_result_size];
959 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
960 g_string_assign(result, js_result_utf8);
963 JSStringRelease(js_result_string);
967 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
969 JSStringRelease(var_name);
970 JSStringRelease(js_script);
974 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
975 if (argv_idx(argv, 0))
976 eval_js(web_view, argv_idx(argv, 0), result);
980 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
982 if (argv_idx(argv, 0)) {
983 GArray* lines = read_file_by_line (argv_idx (argv, 0));
988 while ((line = g_array_index(lines, gchar*, i))) {
990 js = g_strdup (line);
992 gchar* newjs = g_strconcat (js, line, NULL);
999 if (uzbl.state.verbose)
1000 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1002 if (argv_idx (argv, 1)) {
1003 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1007 eval_js (web_view, js, result);
1009 g_array_free (lines, TRUE);
1014 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1015 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1016 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1017 webkit_web_view_unmark_text_matches (page);
1018 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1019 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1023 if (uzbl.state.searchtx) {
1024 if (uzbl.state.verbose)
1025 printf ("Searching: %s\n", uzbl.state.searchtx);
1026 webkit_web_view_set_highlight_text_matches (page, TRUE);
1027 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1032 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1034 search_text(page, argv, TRUE);
1038 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1040 search_text(page, argv, FALSE);
1044 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1045 (void) argv; (void) result;
1046 webkit_web_view_set_highlight_text_matches (page, FALSE);
1051 new_window_load_uri (const gchar * uri) {
1052 GString* to_execute = g_string_new ("");
1053 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1055 for (i = 0; entries[i].long_name != NULL; i++) {
1056 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1057 gchar** str = (gchar**)entries[i].arg_data;
1059 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1063 if (uzbl.state.verbose)
1064 printf("\n%s\n", to_execute->str);
1065 g_spawn_command_line_async (to_execute->str, NULL);
1066 g_string_free (to_execute, TRUE);
1070 chain (WebKitWebView *page, GArray *argv, GString *result) {
1071 (void) page; (void) result;
1073 gchar **parts = NULL;
1075 while ((a = argv_idx(argv, i++))) {
1076 parts = g_strsplit (a, " ", 2);
1078 parse_command(parts[0], parts[1], result);
1084 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1088 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1094 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1098 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1104 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1109 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1111 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1116 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1123 /* --Statusbar functions-- */
1125 build_progressbar_ascii(int percent) {
1126 int width=uzbl.gui.sbar.progress_w;
1129 GString *bar = g_string_new("");
1131 l = (double)percent*((double)width/100.);
1132 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1134 for(i=0; i<(int)l; i++)
1135 g_string_append(bar, uzbl.gui.sbar.progress_s);
1138 g_string_append(bar, uzbl.gui.sbar.progress_u);
1140 return g_string_free(bar, FALSE);
1145 const GScannerConfig scan_config = {
1148 ) /* cset_skip_characters */,
1153 ) /* cset_identifier_first */,
1160 ) /* cset_identifier_nth */,
1161 ( "" ) /* cpair_comment_single */,
1163 TRUE /* case_sensitive */,
1165 FALSE /* skip_comment_multi */,
1166 FALSE /* skip_comment_single */,
1167 FALSE /* scan_comment_multi */,
1168 TRUE /* scan_identifier */,
1169 TRUE /* scan_identifier_1char */,
1170 FALSE /* scan_identifier_NULL */,
1171 TRUE /* scan_symbols */,
1172 FALSE /* scan_binary */,
1173 FALSE /* scan_octal */,
1174 FALSE /* scan_float */,
1175 FALSE /* scan_hex */,
1176 FALSE /* scan_hex_dollar */,
1177 FALSE /* scan_string_sq */,
1178 FALSE /* scan_string_dq */,
1179 TRUE /* numbers_2_int */,
1180 FALSE /* int_2_float */,
1181 FALSE /* identifier_2_string */,
1182 FALSE /* char_2_token */,
1183 FALSE /* symbol_2_token */,
1184 TRUE /* scope_0_fallback */,
1189 uzbl.scan = g_scanner_new(&scan_config);
1190 while(symp->symbol_name) {
1191 g_scanner_scope_add_symbol(uzbl.scan, 0,
1193 GINT_TO_POINTER(symp->symbol_token));
1199 expand_template(const char *template, gboolean escape_markup) {
1200 if(!template) return NULL;
1202 GTokenType token = G_TOKEN_NONE;
1203 GString *ret = g_string_new("");
1207 g_scanner_input_text(uzbl.scan, template, strlen(template));
1208 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1209 token = g_scanner_get_next_token(uzbl.scan);
1211 if(token == G_TOKEN_SYMBOL) {
1212 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1216 buf = uzbl.state.uri ?
1217 g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1218 g_string_append(ret, buf);
1222 g_string_append(ret, uzbl.state.uri ?
1223 uzbl.state.uri : g_strdup(""));
1226 buf = itos(uzbl.gui.sbar.load_progress);
1227 g_string_append(ret, buf);
1230 case SYM_LOADPRGSBAR:
1231 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1232 g_string_append(ret, buf);
1237 buf = uzbl.gui.main_title ?
1238 g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1239 g_string_append(ret, buf);
1243 g_string_append(ret, uzbl.gui.main_title ?
1244 uzbl.gui.main_title : "");
1246 case SYM_SELECTED_URI:
1248 buf = uzbl.state.selected_url ?
1249 g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1250 g_string_append(ret, buf);
1254 g_string_append(ret, uzbl.state.selected_url ?
1255 uzbl.state.selected_url : "");
1258 buf = itos(uzbl.xwin);
1259 g_string_append(ret,
1260 uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1265 buf = uzbl.state.keycmd->str ?
1266 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1267 g_string_append(ret, buf);
1271 g_string_append(ret, uzbl.state.keycmd->str ?
1272 uzbl.state.keycmd->str : "");
1275 g_string_append(ret,
1276 uzbl.behave.insert_mode ?
1277 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1280 g_string_append(ret,
1281 uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1283 /* useragent syms */
1285 buf = itos(WEBKIT_MAJOR_VERSION);
1286 g_string_append(ret, buf);
1290 buf = itos(WEBKIT_MINOR_VERSION);
1291 g_string_append(ret, buf);
1295 buf = itos(WEBKIT_MICRO_VERSION);
1296 g_string_append(ret, buf);
1300 g_string_append(ret, uzbl.state.unameinfo.sysname);
1303 g_string_append(ret, uzbl.state.unameinfo.nodename);
1306 g_string_append(ret, uzbl.state.unameinfo.release);
1309 g_string_append(ret, uzbl.state.unameinfo.version);
1312 g_string_append(ret, uzbl.state.unameinfo.machine);
1315 g_string_append(ret, ARCH);
1318 case SYM_DOMAINNAME:
1319 g_string_append(ret, uzbl.state.unameinfo.domainname);
1323 g_string_append(ret, COMMIT);
1329 else if(token == G_TOKEN_INT) {
1330 g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1332 else if(token == G_TOKEN_IDENTIFIER) {
1333 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1335 else if(token == G_TOKEN_CHAR) {
1336 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1338 else if(token == G_TOKEN_ERROR) {
1339 g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.",
1341 g_scanner_cur_line(uzbl.scan),
1342 g_scanner_cur_position(uzbl.scan));
1346 return g_string_free(ret, FALSE);
1348 /* --End Statusbar functions-- */
1351 sharg_append(GArray *a, const gchar *str) {
1352 const gchar *s = (str ? str : "");
1353 g_array_append_val(a, s);
1356 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1358 run_command (const gchar *command, const guint npre, const gchar **args,
1359 const gboolean sync, char **output_stdout) {
1360 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1363 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1364 gchar *pid = itos(getpid());
1365 gchar *xwin = itos(uzbl.xwin);
1367 sharg_append(a, command);
1368 for (i = 0; i < npre; i++) /* add n args before the default vars */
1369 sharg_append(a, args[i]);
1370 sharg_append(a, uzbl.state.config_file);
1371 sharg_append(a, pid);
1372 sharg_append(a, xwin);
1373 sharg_append(a, uzbl.comm.fifo_path);
1374 sharg_append(a, uzbl.comm.socket_path);
1375 sharg_append(a, uzbl.state.uri);
1376 sharg_append(a, uzbl.gui.main_title);
1378 for (i = npre; i < g_strv_length((gchar**)args); i++)
1379 sharg_append(a, args[i]);
1383 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1385 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1386 NULL, NULL, output_stdout, NULL, NULL, &err);
1387 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1388 NULL, NULL, NULL, &err);
1390 if (uzbl.state.verbose) {
1391 GString *s = g_string_new("spawned:");
1392 for (i = 0; i < (a->len); i++) {
1393 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1394 g_string_append_printf(s, " %s", qarg);
1397 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1398 printf("%s\n", s->str);
1399 g_string_free(s, TRUE);
1401 printf("Stdout: %s\n", *output_stdout);
1405 g_printerr("error on run_command: %s\n", err->message);
1410 g_array_free (a, TRUE);
1415 split_quoted(const gchar* src, const gboolean unquote) {
1416 /* split on unquoted space, return array of strings;
1417 remove a layer of quotes and backslashes if unquote */
1418 if (!src) return NULL;
1420 gboolean dq = FALSE;
1421 gboolean sq = FALSE;
1422 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1423 GString *s = g_string_new ("");
1427 for (p = src; *p != '\0'; p++) {
1428 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1429 else if (*p == '\\') { g_string_append_c(s, *p++);
1430 g_string_append_c(s, *p); }
1431 else if ((*p == '"') && unquote && !sq) dq = !dq;
1432 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1434 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1435 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1437 else if ((*p == ' ') && !dq && !sq) {
1438 dup = g_strdup(s->str);
1439 g_array_append_val(a, dup);
1440 g_string_truncate(s, 0);
1441 } else g_string_append_c(s, *p);
1443 dup = g_strdup(s->str);
1444 g_array_append_val(a, dup);
1445 ret = (gchar**)a->data;
1446 g_array_free (a, FALSE);
1447 g_string_free (s, TRUE);
1452 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1453 (void)web_view; (void)result;
1454 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1455 if (argv_idx(argv, 0))
1456 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1460 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1461 (void)web_view; (void)result;
1463 if (argv_idx(argv, 0))
1464 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1465 TRUE, &uzbl.comm.sync_stdout);
1469 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1470 (void)web_view; (void)result;
1471 if (!uzbl.behave.shell_cmd) {
1472 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1477 gchar *spacer = g_strdup("");
1478 g_array_insert_val(argv, 1, spacer);
1479 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1481 for (i = 1; i < g_strv_length(cmd); i++)
1482 g_array_prepend_val(argv, cmd[i]);
1484 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1490 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1491 (void)web_view; (void)result;
1492 if (!uzbl.behave.shell_cmd) {
1493 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1498 gchar *spacer = g_strdup("");
1499 g_array_insert_val(argv, 1, spacer);
1500 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1502 for (i = 1; i < g_strv_length(cmd); i++)
1503 g_array_prepend_val(argv, cmd[i]);
1505 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1506 TRUE, &uzbl.comm.sync_stdout);
1512 parse_command(const char *cmd, const char *param, GString *result) {
1515 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1517 gchar **par = split_quoted(param, TRUE);
1518 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1520 if (c->no_split) { /* don't split */
1521 sharg_append(a, param);
1523 for (i = 0; i < g_strv_length(par); i++)
1524 sharg_append(a, par[i]);
1527 if (result == NULL) {
1528 GString *result_print = g_string_new("");
1530 c->function(uzbl.gui.web_view, a, result_print);
1531 if (result_print->len)
1532 printf("%*s\n", result_print->len, result_print->str);
1534 g_string_free(result_print, TRUE);
1536 c->function(uzbl.gui.web_view, a, result);
1539 g_array_free (a, TRUE);
1542 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1549 if(*uzbl.net.proxy_url == ' '
1550 || uzbl.net.proxy_url == NULL) {
1551 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1552 (GType) SOUP_SESSION_PROXY_URI);
1555 suri = soup_uri_new(uzbl.net.proxy_url);
1556 g_object_set(G_OBJECT(uzbl.net.soup_session),
1557 SOUP_SESSION_PROXY_URI,
1559 soup_uri_free(suri);
1566 if(file_exists(uzbl.gui.icon)) {
1567 if (uzbl.gui.main_window)
1568 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1570 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1576 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1577 g_array_append_val (a, uzbl.state.uri);
1578 load_uri(uzbl.gui.web_view, a, NULL);
1579 g_array_free (a, TRUE);
1583 cmd_always_insert_mode() {
1584 uzbl.behave.insert_mode =
1585 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1591 g_object_set(G_OBJECT(uzbl.net.soup_session),
1592 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1596 cmd_max_conns_host() {
1597 g_object_set(G_OBJECT(uzbl.net.soup_session),
1598 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1603 soup_session_remove_feature
1604 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1605 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1606 /*g_free(uzbl.net.soup_logger);*/
1608 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1609 soup_session_add_feature(uzbl.net.soup_session,
1610 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1615 return webkit_web_view_get_settings(uzbl.gui.web_view);
1620 WebKitWebSettings *ws = view_settings();
1621 if (uzbl.behave.font_size > 0) {
1622 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1625 if (uzbl.behave.monospace_size > 0) {
1626 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1627 uzbl.behave.monospace_size, NULL);
1629 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1630 uzbl.behave.font_size, NULL);
1636 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1640 cmd_disable_plugins() {
1641 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1642 !uzbl.behave.disable_plugins, NULL);
1646 cmd_disable_scripts() {
1647 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1648 !uzbl.behave.disable_scripts, NULL);
1652 cmd_minimum_font_size() {
1653 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1654 uzbl.behave.minimum_font_size, NULL);
1657 cmd_autoload_img() {
1658 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1659 uzbl.behave.autoload_img, NULL);
1664 cmd_autoshrink_img() {
1665 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1666 uzbl.behave.autoshrink_img, NULL);
1671 cmd_enable_spellcheck() {
1672 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1673 uzbl.behave.enable_spellcheck, NULL);
1677 cmd_enable_private() {
1678 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1679 uzbl.behave.enable_private, NULL);
1684 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1685 uzbl.behave.print_bg, NULL);
1690 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1691 uzbl.behave.style_uri, NULL);
1695 cmd_resizable_txt() {
1696 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1697 uzbl.behave.resizable_txt, NULL);
1701 cmd_default_encoding() {
1702 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1703 uzbl.behave.default_encoding, NULL);
1707 cmd_enforce_96dpi() {
1708 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1709 uzbl.behave.enforce_96dpi, NULL);
1713 cmd_caret_browsing() {
1714 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1715 uzbl.behave.caret_browsing, NULL);
1719 cmd_cookie_handler() {
1720 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1721 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1722 if ((g_strcmp0(split[0], "sh") == 0) ||
1723 (g_strcmp0(split[0], "spawn") == 0)) {
1724 g_free (uzbl.behave.cookie_handler);
1725 uzbl.behave.cookie_handler =
1726 g_strdup_printf("sync_%s %s", split[0], split[1]);
1733 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1738 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1743 if(uzbl.behave.inject_html) {
1744 webkit_web_view_load_html_string (uzbl.gui.web_view,
1745 uzbl.behave.inject_html, NULL);
1754 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1755 uzbl.behave.modmask = 0;
1757 if(uzbl.behave.modkey)
1758 g_free(uzbl.behave.modkey);
1759 uzbl.behave.modkey = buf;
1761 for (i = 0; modkeys[i].key != NULL; i++) {
1762 if (g_strrstr(buf, modkeys[i].key))
1763 uzbl.behave.modmask |= modkeys[i].mask;
1769 if (*uzbl.net.useragent == ' ') {
1770 g_free (uzbl.net.useragent);
1771 uzbl.net.useragent = NULL;
1773 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1775 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1776 g_free(uzbl.net.useragent);
1777 uzbl.net.useragent = ua;
1783 gtk_widget_ref(uzbl.gui.scrolled_win);
1784 gtk_widget_ref(uzbl.gui.mainbar);
1785 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1786 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1788 if(uzbl.behave.status_top) {
1789 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1790 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1793 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1794 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1796 gtk_widget_unref(uzbl.gui.scrolled_win);
1797 gtk_widget_unref(uzbl.gui.mainbar);
1798 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1803 set_var_value(gchar *name, gchar *val) {
1804 uzbl_cmdprop *c = NULL;
1808 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1809 /* check for the variable type */
1810 if (c->type == TYPE_STR) {
1811 buf = expand(val, 0);
1814 } else if(c->type == TYPE_INT) {
1815 int *ip = (int *)c->ptr;
1816 buf = expand(val, 0);
1817 *ip = (int)strtoul(buf, &endp, 10);
1819 } else if (c->type == TYPE_FLOAT) {
1820 float *fp = (float *)c->ptr;
1821 buf = expand(val, 0);
1822 *fp = strtod(buf, &endp);
1826 /* invoke a command specific function */
1827 if(c->func) c->func();
1834 Behaviour *b = &uzbl.behave;
1836 if(b->html_buffer->str) {
1837 webkit_web_view_load_html_string (uzbl.gui.web_view,
1838 b->html_buffer->str, b->base_url);
1839 g_string_free(b->html_buffer, TRUE);
1840 b->html_buffer = g_string_new("");
1844 enum {M_CMD, M_HTML};
1846 parse_cmd_line(const char *ctl_line, GString *result) {
1847 Behaviour *b = &uzbl.behave;
1850 if(b->mode == M_HTML) {
1851 len = strlen(b->html_endmarker);
1852 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1853 if(len == strlen(ctl_line)-1 &&
1854 !strncmp(b->html_endmarker, ctl_line, len)) {
1856 set_var_value("mode", "0");
1861 set_timeout(b->html_timeout);
1862 g_string_append(b->html_buffer, ctl_line);
1865 else if((ctl_line[0] == '#') /* Comments */
1866 || (ctl_line[0] == ' ')
1867 || (ctl_line[0] == '\n'))
1868 ; /* ignore these lines */
1869 else { /* parse a command */
1871 gchar **tokens = NULL;
1872 len = strlen(ctl_line);
1874 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1875 ctlstrip = g_strndup(ctl_line, len - 1);
1876 else ctlstrip = g_strdup(ctl_line);
1878 tokens = g_strsplit(ctlstrip, " ", 2);
1879 parse_command(tokens[0], tokens[1], result);
1886 build_stream_name(int type, const gchar* dir) {
1887 char *xwin_str = NULL;
1888 State *s = &uzbl.state;
1891 xwin_str = itos((int)uzbl.xwin);
1893 str = g_strdup_printf
1894 ("%s/uzbl_fifo_%s", dir,
1895 s->instance_name ? s->instance_name : xwin_str);
1896 } else if (type == SOCKET) {
1897 str = g_strdup_printf
1898 ("%s/uzbl_socket_%s", dir,
1899 s->instance_name ? s->instance_name : xwin_str );
1906 control_fifo(GIOChannel *gio, GIOCondition condition) {
1907 if (uzbl.state.verbose)
1908 printf("triggered\n");
1913 if (condition & G_IO_HUP)
1914 g_error ("Fifo: Read end of pipe died!\n");
1917 g_error ("Fifo: GIOChannel broke\n");
1919 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1920 if (ret == G_IO_STATUS_ERROR) {
1921 g_error ("Fifo: Error reading: %s\n", err->message);
1925 parse_cmd_line(ctl_line, NULL);
1932 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1933 GIOChannel *chan = NULL;
1934 GError *error = NULL;
1935 gchar *path = build_stream_name(FIFO, dir);
1937 if (!file_exists(path)) {
1938 if (mkfifo (path, 0666) == 0) {
1939 // 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.
1940 chan = g_io_channel_new_file(path, "r+", &error);
1942 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1943 if (uzbl.state.verbose)
1944 printf ("init_fifo: created successfully as %s\n", path);
1945 uzbl.comm.fifo_path = path;
1947 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1948 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1949 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1950 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1952 /* if we got this far, there was an error; cleanup */
1953 if (error) g_error_free (error);
1960 control_stdin(GIOChannel *gio, GIOCondition condition) {
1962 gchar *ctl_line = NULL;
1965 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1966 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1969 parse_cmd_line(ctl_line, NULL);
1977 GIOChannel *chan = NULL;
1978 GError *error = NULL;
1980 chan = g_io_channel_unix_new(fileno(stdin));
1982 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1983 g_error ("Stdin: could not add watch\n");
1985 if (uzbl.state.verbose)
1986 printf ("Stdin: watch added successfully\n");
1989 g_error ("Stdin: Error while opening: %s\n", error->message);
1991 if (error) g_error_free (error);
1995 control_socket(GIOChannel *chan) {
1996 struct sockaddr_un remote;
1997 unsigned int t = sizeof(remote);
1999 GIOChannel *clientchan;
2001 clientsock = accept (g_io_channel_unix_get_fd(chan),
2002 (struct sockaddr *) &remote, &t);
2004 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2005 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2006 (GIOFunc) control_client_socket, clientchan);
2013 control_client_socket(GIOChannel *clientchan) {
2015 GString *result = g_string_new("");
2016 GError *error = NULL;
2020 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2021 if (ret == G_IO_STATUS_ERROR) {
2022 g_warning ("Error reading: %s\n", error->message);
2023 g_io_channel_shutdown(clientchan, TRUE, &error);
2025 } else if (ret == G_IO_STATUS_EOF) {
2026 /* shutdown and remove channel watch from main loop */
2027 g_io_channel_shutdown(clientchan, TRUE, &error);
2032 parse_cmd_line (ctl_line, result);
2033 g_string_append_c(result, '\n');
2034 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2036 if (ret == G_IO_STATUS_ERROR) {
2037 g_warning ("Error writing: %s", error->message);
2039 g_io_channel_flush(clientchan, &error);
2042 if (error) g_error_free (error);
2043 g_string_free(result, TRUE);
2049 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2050 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2051 if (unlink(uzbl.comm.socket_path) == -1)
2052 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2053 g_free(uzbl.comm.socket_path);
2054 uzbl.comm.socket_path = NULL;
2062 GIOChannel *chan = NULL;
2064 struct sockaddr_un local;
2065 gchar *path = build_stream_name(SOCKET, dir);
2067 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2069 local.sun_family = AF_UNIX;
2070 strcpy (local.sun_path, path);
2071 unlink (local.sun_path);
2073 len = strlen (local.sun_path) + sizeof (local.sun_family);
2074 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2075 if (uzbl.state.verbose)
2076 printf ("init_socket: opened in %s\n", path);
2079 if( (chan = g_io_channel_unix_new(sock)) ) {
2080 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2081 uzbl.comm.socket_path = path;
2084 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2086 /* if we got this far, there was an error; cleanup */
2093 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2094 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2096 // this function may be called very early when the templates are not set (yet), hence the checks
2098 update_title (void) {
2099 Behaviour *b = &uzbl.behave;
2102 if (b->show_status) {
2103 if (b->title_format_short) {
2104 parsed = expand_template(b->title_format_short, FALSE);
2105 if (uzbl.gui.main_window)
2106 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2109 if (b->status_format) {
2110 parsed = expand_template(b->status_format, TRUE);
2111 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2114 if (b->status_background) {
2116 gdk_color_parse (b->status_background, &color);
2117 //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)
2118 if (uzbl.gui.main_window)
2119 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2120 else if (uzbl.gui.plug)
2121 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2124 if (b->title_format_long) {
2125 parsed = expand_template(b->title_format_long, FALSE);
2126 if (uzbl.gui.main_window)
2127 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2134 key_press_cb (GtkWidget* window, GdkEventKey* event)
2136 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2140 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2141 || 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)
2144 /* turn off insert mode (if always_insert_mode is not used) */
2145 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2146 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2151 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2154 if (event->keyval == GDK_Escape) {
2155 g_string_truncate(uzbl.state.keycmd, 0);
2157 dehilight(uzbl.gui.web_view, NULL, NULL);
2161 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2162 if (event->keyval == GDK_Insert) {
2164 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2165 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2167 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2170 g_string_append (uzbl.state.keycmd, str);
2177 if (event->keyval == GDK_BackSpace)
2178 keycmd_bs(NULL, NULL, NULL);
2180 gboolean key_ret = FALSE;
2181 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2183 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2185 run_keycmd(key_ret);
2187 if (key_ret) return (!uzbl.behave.insert_mode);
2192 run_keycmd(const gboolean key_ret) {
2193 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2195 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2196 g_string_truncate(uzbl.state.keycmd, 0);
2197 parse_command(act->name, act->param, NULL);
2201 /* try if it's an incremental keycmd or one that takes args, and run it */
2202 GString* short_keys = g_string_new ("");
2203 GString* short_keys_inc = g_string_new ("");
2205 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2206 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2207 g_string_assign(short_keys_inc, short_keys->str);
2208 g_string_append_c(short_keys, '_');
2209 g_string_append_c(short_keys_inc, '*');
2211 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2212 /* run normal cmds only if return was pressed */
2213 exec_paramcmd(act, i);
2214 g_string_truncate(uzbl.state.keycmd, 0);
2216 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2217 if (key_ret) /* just quit the incremental command on return */
2218 g_string_truncate(uzbl.state.keycmd, 0);
2219 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2223 g_string_truncate(short_keys, short_keys->len - 1);
2225 g_string_free (short_keys, TRUE);
2226 g_string_free (short_keys_inc, TRUE);
2230 exec_paramcmd(const Action *act, const guint i) {
2231 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2232 GString *actionname = g_string_new ("");
2233 GString *actionparam = g_string_new ("");
2234 g_string_erase (parampart, 0, i+1);
2236 g_string_printf (actionname, act->name, parampart->str);
2238 g_string_printf (actionparam, act->param, parampart->str);
2239 parse_command(actionname->str, actionparam->str, NULL);
2240 g_string_free(actionname, TRUE);
2241 g_string_free(actionparam, TRUE);
2242 g_string_free(parampart, TRUE);
2250 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2251 //main_window_ref = g_object_ref(scrolled_window);
2252 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
2254 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2255 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2257 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2258 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2259 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2269 return scrolled_window;
2276 g->mainbar = gtk_hbox_new (FALSE, 0);
2278 /* keep a reference to the bar so we can re-pack it at runtime*/
2279 //sbar_ref = g_object_ref(g->mainbar);
2281 g->mainbar_label = gtk_label_new ("");
2282 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2283 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2284 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2285 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2286 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2287 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2293 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2294 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2295 gtk_widget_set_name (window, "Uzbl browser");
2296 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2297 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2304 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2305 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2306 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2313 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2315 If actname is one that calls an external command, this function will inject
2316 newargs in front of the user-provided args in that command line. They will
2317 come become after the body of the script (in sh) or after the name of
2318 the command to execute (in spawn).
2319 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2320 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2322 The return value consist of two strings: the action (sh, ...) and its args.
2324 If act is not one that calls an external command, then the given action merely
2327 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2328 /* Arrr! Here be memory leaks */
2329 gchar *actdup = g_strdup(actname);
2330 g_array_append_val(rets, actdup);
2332 if ((g_strcmp0(actname, "spawn") == 0) ||
2333 (g_strcmp0(actname, "sh") == 0) ||
2334 (g_strcmp0(actname, "sync_spawn") == 0) ||
2335 (g_strcmp0(actname, "sync_sh") == 0)) {
2337 GString *a = g_string_new("");
2338 gchar **spawnparts = split_quoted(origargs, FALSE);
2339 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2340 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2342 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2343 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2345 g_array_append_val(rets, a->str);
2346 g_string_free(a, FALSE);
2347 g_strfreev(spawnparts);
2349 gchar *origdup = g_strdup(origargs);
2350 g_array_append_val(rets, origdup);
2352 return (gchar**)g_array_free(rets, FALSE);
2356 run_handler (const gchar *act, const gchar *args) {
2357 /* Consider this code a temporary hack to make the handlers usable.
2358 In practice, all this splicing, injection, and reconstruction is
2359 inefficient, annoying and hard to manage. Potential pitfalls arise
2360 when the handler specific args 1) are not quoted (the handler
2361 callbacks should take care of this) 2) are quoted but interfere
2362 with the users' own quotation. A more ideal solution is
2363 to refactor parse_command so that it doesn't just take a string
2364 and execute it; rather than that, we should have a function which
2365 returns the argument vector parsed from the string. This vector
2366 could be modified (e.g. insert additional args into it) before
2367 passing it to the next function that actually executes it. Though
2368 it still isn't perfect for chain actions.. will reconsider & re-
2369 factor when I have the time. -duc */
2371 char **parts = g_strsplit(act, " ", 2);
2373 if (g_strcmp0(parts[0], "chain") == 0) {
2374 GString *newargs = g_string_new("");
2375 gchar **chainparts = split_quoted(parts[1], FALSE);
2377 /* for every argument in the chain, inject the handler args
2378 and make sure the new parts are wrapped in quotes */
2379 gchar **cp = chainparts;
2381 gchar *quotless = NULL;
2382 gchar **spliced_quotless = NULL; // sigh -_-;
2383 gchar **inpart = NULL;
2386 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2388 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2389 } else quotless = g_strdup(*cp);
2391 spliced_quotless = g_strsplit(quotless, " ", 2);
2392 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2393 g_strfreev(spliced_quotless);
2395 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2401 parse_command(parts[0], &(newargs->str[1]), NULL);
2402 g_string_free(newargs, TRUE);
2403 g_strfreev(chainparts);
2406 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2407 parse_command(inparts[0], inparts[1], NULL);
2415 add_binding (const gchar *key, const gchar *act) {
2416 char **parts = g_strsplit(act, " ", 2);
2423 if (uzbl.state.verbose)
2424 printf ("Binding %-10s : %s\n", key, act);
2425 action = new_action(parts[0], parts[1]);
2427 if (g_hash_table_remove (uzbl.bindings, key))
2428 g_warning ("Overwriting existing binding for \"%s\"", key);
2429 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2434 get_xdg_var (XDG_Var xdg) {
2435 const gchar* actual_value = getenv (xdg.environmental);
2436 const gchar* home = getenv ("HOME");
2437 gchar* return_value;
2439 if (! actual_value || strcmp (actual_value, "") == 0) {
2440 if (xdg.default_value) {
2441 return_value = str_replace ("~", home, xdg.default_value);
2443 return_value = NULL;
2446 return_value = str_replace("~", home, actual_value);
2449 return return_value;
2453 find_xdg_file (int xdg_type, char* filename) {
2454 /* xdg_type = 0 => config
2455 xdg_type = 1 => data
2456 xdg_type = 2 => cache*/
2458 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2459 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2462 gchar* temporary_string;
2466 if (! file_exists (temporary_file) && xdg_type != 2) {
2467 buf = get_xdg_var (XDG[3 + xdg_type]);
2468 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2471 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2472 g_free (temporary_file);
2473 temporary_file = g_strconcat (temporary_string, filename, NULL);
2477 //g_free (temporary_string); - segfaults.
2479 if (file_exists (temporary_file)) {
2480 return temporary_file;
2487 State *s = &uzbl.state;
2488 Network *n = &uzbl.net;
2490 for (i = 0; default_config[i].command != NULL; i++) {
2491 parse_cmd_line(default_config[i].command, NULL);
2494 if (g_strcmp0(s->config_file, "-") == 0) {
2495 s->config_file = NULL;
2499 else if (!s->config_file) {
2500 s->config_file = find_xdg_file (0, "/uzbl/config");
2503 if (s->config_file) {
2504 GArray* lines = read_file_by_line (s->config_file);
2508 while ((line = g_array_index(lines, gchar*, i))) {
2509 parse_cmd_line (line, NULL);
2513 g_array_free (lines, TRUE);
2515 if (uzbl.state.verbose)
2516 printf ("No configuration file loaded.\n");
2519 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2522 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2525 if (!uzbl.behave.cookie_handler)
2528 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2529 GString *s = g_string_new ("");
2530 SoupURI * soup_uri = soup_message_get_uri(msg);
2531 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2532 run_handler(uzbl.behave.cookie_handler, s->str);
2534 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2535 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2536 if ( p != NULL ) *p = '\0';
2537 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2539 if (uzbl.comm.sync_stdout)
2540 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2542 g_string_free(s, TRUE);
2546 save_cookies (SoupMessage *msg, gpointer user_data){
2550 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2551 cookie = soup_cookie_to_set_cookie_header(ck->data);
2552 SoupURI * soup_uri = soup_message_get_uri(msg);
2553 GString *s = g_string_new ("");
2554 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2555 run_handler(uzbl.behave.cookie_handler, s->str);
2557 g_string_free(s, TRUE);
2562 /* --- WEBINSPECTOR --- */
2564 hide_window_cb(GtkWidget *widget, gpointer data) {
2567 gtk_widget_hide(widget);
2571 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2574 (void) web_inspector;
2575 GtkWidget* scrolled_window;
2576 GtkWidget* new_web_view;
2579 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2580 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2581 G_CALLBACK(hide_window_cb), NULL);
2583 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2584 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2585 gtk_widget_show(g->inspector_window);
2587 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2588 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2589 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2590 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2591 gtk_widget_show(scrolled_window);
2593 new_web_view = webkit_web_view_new();
2594 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2596 return WEBKIT_WEB_VIEW(new_web_view);
2600 inspector_show_window_cb (WebKitWebInspector* inspector){
2602 gtk_widget_show(uzbl.gui.inspector_window);
2606 /* TODO: Add variables and code to make use of these functions */
2608 inspector_close_window_cb (WebKitWebInspector* inspector){
2614 inspector_attach_window_cb (WebKitWebInspector* inspector){
2620 inspector_detach_window_cb (WebKitWebInspector* inspector){
2626 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2632 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2638 set_up_inspector() {
2640 WebKitWebSettings *settings = view_settings();
2641 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2643 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2644 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2645 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2646 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2647 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2648 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2649 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2651 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2655 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2657 uzbl_cmdprop *c = v;
2662 if(c->type == TYPE_STR)
2663 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2664 else if(c->type == TYPE_INT)
2665 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2666 else if(c->type == TYPE_FLOAT)
2667 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2671 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2675 printf("bind %s = %s %s\n", (char *)k ,
2676 (char *)a->name, a->param?(char *)a->param:"");
2681 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2682 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2685 #ifndef UZBL_LIBRARY
2688 main (int argc, char* argv[]) {
2689 gtk_init (&argc, &argv);
2690 if (!g_thread_supported ())
2691 g_thread_init (NULL);
2692 uzbl.state.executable_path = g_strdup(argv[0]);
2693 uzbl.state.selected_url = NULL;
2694 uzbl.state.searchtx = NULL;
2696 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2697 g_option_context_add_main_entries (context, entries, NULL);
2698 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2699 g_option_context_parse (context, &argc, &argv, NULL);
2700 g_option_context_free(context);
2702 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2703 gboolean verbose_override = uzbl.state.verbose;
2705 /* initialize hash table */
2706 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2708 uzbl.net.soup_session = webkit_get_default_session();
2709 uzbl.state.keycmd = g_string_new("");
2711 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2712 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2713 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2714 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2715 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2716 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2719 if(uname(&uzbl.state.unameinfo) == -1)
2720 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2722 uzbl.gui.sbar.progress_s = g_strdup("=");
2723 uzbl.gui.sbar.progress_u = g_strdup("·");
2724 uzbl.gui.sbar.progress_w = 10;
2726 /* HTML mode defaults*/
2727 uzbl.behave.html_buffer = g_string_new("");
2728 uzbl.behave.html_endmarker = g_strdup(".");
2729 uzbl.behave.html_timeout = 60;
2730 uzbl.behave.base_url = g_strdup("http://invalid");
2732 /* default mode indicators */
2733 uzbl.behave.insert_indicator = g_strdup("I");
2734 uzbl.behave.cmd_indicator = g_strdup("C");
2738 make_var_to_name_hash();
2740 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2742 uzbl.gui.scrolled_win = create_browser();
2745 /* initial packing */
2746 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2747 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2749 if (uzbl.state.socket_id) {
2750 uzbl.gui.plug = create_plug ();
2751 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2752 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2754 uzbl.gui.main_window = create_window ();
2755 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2756 gtk_widget_show_all (uzbl.gui.main_window);
2757 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2760 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2762 if (uzbl.state.verbose) {
2763 printf("Uzbl start location: %s\n", argv[0]);
2764 if (uzbl.state.socket_id)
2765 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2767 printf("window_id %i\n",(int) uzbl.xwin);
2768 printf("pid %i\n", getpid ());
2769 printf("name: %s\n", uzbl.state.instance_name);
2772 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2773 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2774 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2775 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2776 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2780 if (!uzbl.behave.show_status)
2781 gtk_widget_hide(uzbl.gui.mainbar);
2788 if (verbose_override > uzbl.state.verbose)
2789 uzbl.state.verbose = verbose_override;
2792 set_var_value("uri", uri_override);
2793 g_free(uri_override);
2794 } else if (uzbl.state.uri)
2795 cmd_load_uri(uzbl.gui.web_view, NULL);
2800 return EXIT_SUCCESS;
2804 /* vi: set et ts=4: */