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 is ok as we 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);
2122 if (b->title_format_long) {
2123 parsed = expand_template(b->title_format_long, FALSE);
2124 if (uzbl.gui.main_window)
2125 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2132 key_press_cb (GtkWidget* window, GdkEventKey* event)
2134 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2138 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2139 || 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)
2142 /* turn off insert mode (if always_insert_mode is not used) */
2143 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2144 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2149 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2152 if (event->keyval == GDK_Escape) {
2153 g_string_truncate(uzbl.state.keycmd, 0);
2155 dehilight(uzbl.gui.web_view, NULL, NULL);
2159 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2160 if (event->keyval == GDK_Insert) {
2162 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2163 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2165 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2168 g_string_append (uzbl.state.keycmd, str);
2175 if (event->keyval == GDK_BackSpace)
2176 keycmd_bs(NULL, NULL, NULL);
2178 gboolean key_ret = FALSE;
2179 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2181 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2183 run_keycmd(key_ret);
2185 if (key_ret) return (!uzbl.behave.insert_mode);
2190 run_keycmd(const gboolean key_ret) {
2191 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2193 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2194 g_string_truncate(uzbl.state.keycmd, 0);
2195 parse_command(act->name, act->param, NULL);
2199 /* try if it's an incremental keycmd or one that takes args, and run it */
2200 GString* short_keys = g_string_new ("");
2201 GString* short_keys_inc = g_string_new ("");
2203 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2204 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2205 g_string_assign(short_keys_inc, short_keys->str);
2206 g_string_append_c(short_keys, '_');
2207 g_string_append_c(short_keys_inc, '*');
2209 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2210 /* run normal cmds only if return was pressed */
2211 exec_paramcmd(act, i);
2212 g_string_truncate(uzbl.state.keycmd, 0);
2214 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2215 if (key_ret) /* just quit the incremental command on return */
2216 g_string_truncate(uzbl.state.keycmd, 0);
2217 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2221 g_string_truncate(short_keys, short_keys->len - 1);
2223 g_string_free (short_keys, TRUE);
2224 g_string_free (short_keys_inc, TRUE);
2228 exec_paramcmd(const Action *act, const guint i) {
2229 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2230 GString *actionname = g_string_new ("");
2231 GString *actionparam = g_string_new ("");
2232 g_string_erase (parampart, 0, i+1);
2234 g_string_printf (actionname, act->name, parampart->str);
2236 g_string_printf (actionparam, act->param, parampart->str);
2237 parse_command(actionname->str, actionparam->str, NULL);
2238 g_string_free(actionname, TRUE);
2239 g_string_free(actionparam, TRUE);
2240 g_string_free(parampart, TRUE);
2248 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2249 //main_window_ref = g_object_ref(scrolled_window);
2250 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
2252 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2253 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2255 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2256 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2257 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2258 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2259 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2267 return scrolled_window;
2274 g->mainbar = gtk_hbox_new (FALSE, 0);
2276 /* keep a reference to the bar so we can re-pack it at runtime*/
2277 //sbar_ref = g_object_ref(g->mainbar);
2279 g->mainbar_label = gtk_label_new ("");
2280 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2281 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2282 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2283 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2284 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2285 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2291 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2292 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2293 gtk_widget_set_name (window, "Uzbl browser");
2294 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2295 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2302 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2303 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2304 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2311 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2313 If actname is one that calls an external command, this function will inject
2314 newargs in front of the user-provided args in that command line. They will
2315 come become after the body of the script (in sh) or after the name of
2316 the command to execute (in spawn).
2317 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2318 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2320 The return value consist of two strings: the action (sh, ...) and its args.
2322 If act is not one that calls an external command, then the given action merely
2325 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2326 /* Arrr! Here be memory leaks */
2327 gchar *actdup = g_strdup(actname);
2328 g_array_append_val(rets, actdup);
2330 if ((g_strcmp0(actname, "spawn") == 0) ||
2331 (g_strcmp0(actname, "sh") == 0) ||
2332 (g_strcmp0(actname, "sync_spawn") == 0) ||
2333 (g_strcmp0(actname, "sync_sh") == 0)) {
2335 GString *a = g_string_new("");
2336 gchar **spawnparts = split_quoted(origargs, FALSE);
2337 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2338 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2340 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2341 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2343 g_array_append_val(rets, a->str);
2344 g_string_free(a, FALSE);
2345 g_strfreev(spawnparts);
2347 gchar *origdup = g_strdup(origargs);
2348 g_array_append_val(rets, origdup);
2350 return (gchar**)g_array_free(rets, FALSE);
2354 run_handler (const gchar *act, const gchar *args) {
2355 /* Consider this code a temporary hack to make the handlers usable.
2356 In practice, all this splicing, injection, and reconstruction is
2357 inefficient, annoying and hard to manage. Potential pitfalls arise
2358 when the handler specific args 1) are not quoted (the handler
2359 callbacks should take care of this) 2) are quoted but interfere
2360 with the users' own quotation. A more ideal solution is
2361 to refactor parse_command so that it doesn't just take a string
2362 and execute it; rather than that, we should have a function which
2363 returns the argument vector parsed from the string. This vector
2364 could be modified (e.g. insert additional args into it) before
2365 passing it to the next function that actually executes it. Though
2366 it still isn't perfect for chain actions.. will reconsider & re-
2367 factor when I have the time. -duc */
2369 char **parts = g_strsplit(act, " ", 2);
2371 if (g_strcmp0(parts[0], "chain") == 0) {
2372 GString *newargs = g_string_new("");
2373 gchar **chainparts = split_quoted(parts[1], FALSE);
2375 /* for every argument in the chain, inject the handler args
2376 and make sure the new parts are wrapped in quotes */
2377 gchar **cp = chainparts;
2379 gchar *quotless = NULL;
2380 gchar **spliced_quotless = NULL; // sigh -_-;
2381 gchar **inpart = NULL;
2384 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2386 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2387 } else quotless = g_strdup(*cp);
2389 spliced_quotless = g_strsplit(quotless, " ", 2);
2390 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2391 g_strfreev(spliced_quotless);
2393 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2399 parse_command(parts[0], &(newargs->str[1]), NULL);
2400 g_string_free(newargs, TRUE);
2401 g_strfreev(chainparts);
2404 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2405 parse_command(inparts[0], inparts[1], NULL);
2413 add_binding (const gchar *key, const gchar *act) {
2414 char **parts = g_strsplit(act, " ", 2);
2421 if (uzbl.state.verbose)
2422 printf ("Binding %-10s : %s\n", key, act);
2423 action = new_action(parts[0], parts[1]);
2425 if (g_hash_table_remove (uzbl.bindings, key))
2426 g_warning ("Overwriting existing binding for \"%s\"", key);
2427 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2432 get_xdg_var (XDG_Var xdg) {
2433 const gchar* actual_value = getenv (xdg.environmental);
2434 const gchar* home = getenv ("HOME");
2435 gchar* return_value;
2437 if (! actual_value || strcmp (actual_value, "") == 0) {
2438 if (xdg.default_value) {
2439 return_value = str_replace ("~", home, xdg.default_value);
2441 return_value = NULL;
2444 return_value = str_replace("~", home, actual_value);
2447 return return_value;
2451 find_xdg_file (int xdg_type, char* filename) {
2452 /* xdg_type = 0 => config
2453 xdg_type = 1 => data
2454 xdg_type = 2 => cache*/
2456 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2457 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2460 gchar* temporary_string;
2464 if (! file_exists (temporary_file) && xdg_type != 2) {
2465 buf = get_xdg_var (XDG[3 + xdg_type]);
2466 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2469 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2470 g_free (temporary_file);
2471 temporary_file = g_strconcat (temporary_string, filename, NULL);
2475 //g_free (temporary_string); - segfaults.
2477 if (file_exists (temporary_file)) {
2478 return temporary_file;
2485 State *s = &uzbl.state;
2486 Network *n = &uzbl.net;
2488 for (i = 0; default_config[i].command != NULL; i++) {
2489 parse_cmd_line(default_config[i].command, NULL);
2492 if (g_strcmp0(s->config_file, "-") == 0) {
2493 s->config_file = NULL;
2497 else if (!s->config_file) {
2498 s->config_file = find_xdg_file (0, "/uzbl/config");
2501 if (s->config_file) {
2502 GArray* lines = read_file_by_line (s->config_file);
2506 while ((line = g_array_index(lines, gchar*, i))) {
2507 parse_cmd_line (line, NULL);
2511 g_array_free (lines, TRUE);
2513 if (uzbl.state.verbose)
2514 printf ("No configuration file loaded.\n");
2517 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2520 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2523 if (!uzbl.behave.cookie_handler)
2526 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2527 GString *s = g_string_new ("");
2528 SoupURI * soup_uri = soup_message_get_uri(msg);
2529 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2530 run_handler(uzbl.behave.cookie_handler, s->str);
2532 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2533 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2534 if ( p != NULL ) *p = '\0';
2535 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2537 if (uzbl.comm.sync_stdout)
2538 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2540 g_string_free(s, TRUE);
2544 save_cookies (SoupMessage *msg, gpointer user_data){
2548 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2549 cookie = soup_cookie_to_set_cookie_header(ck->data);
2550 SoupURI * soup_uri = soup_message_get_uri(msg);
2551 GString *s = g_string_new ("");
2552 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2553 run_handler(uzbl.behave.cookie_handler, s->str);
2555 g_string_free(s, TRUE);
2560 /* --- WEBINSPECTOR --- */
2562 hide_window_cb(GtkWidget *widget, gpointer data) {
2565 gtk_widget_hide(widget);
2569 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2572 (void) web_inspector;
2573 GtkWidget* scrolled_window;
2574 GtkWidget* new_web_view;
2577 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2578 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2579 G_CALLBACK(hide_window_cb), NULL);
2581 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2582 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2583 gtk_widget_show(g->inspector_window);
2585 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2586 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2587 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2588 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2589 gtk_widget_show(scrolled_window);
2591 new_web_view = webkit_web_view_new();
2592 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2594 return WEBKIT_WEB_VIEW(new_web_view);
2598 inspector_show_window_cb (WebKitWebInspector* inspector){
2600 gtk_widget_show(uzbl.gui.inspector_window);
2604 /* TODO: Add variables and code to make use of these functions */
2606 inspector_close_window_cb (WebKitWebInspector* inspector){
2612 inspector_attach_window_cb (WebKitWebInspector* inspector){
2618 inspector_detach_window_cb (WebKitWebInspector* inspector){
2624 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2630 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2636 set_up_inspector() {
2638 WebKitWebSettings *settings = view_settings();
2639 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2641 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2642 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2643 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2644 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2645 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2646 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2647 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2649 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2653 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2655 uzbl_cmdprop *c = v;
2660 if(c->type == TYPE_STR)
2661 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2662 else if(c->type == TYPE_INT)
2663 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2664 else if(c->type == TYPE_FLOAT)
2665 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2669 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2673 printf("bind %s = %s %s\n", (char *)k ,
2674 (char *)a->name, a->param?(char *)a->param:"");
2679 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2680 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2683 #ifndef UZBL_LIBRARY
2686 main (int argc, char* argv[]) {
2687 gtk_init (&argc, &argv);
2688 if (!g_thread_supported ())
2689 g_thread_init (NULL);
2690 uzbl.state.executable_path = g_strdup(argv[0]);
2691 uzbl.state.selected_url = NULL;
2692 uzbl.state.searchtx = NULL;
2694 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2695 g_option_context_add_main_entries (context, entries, NULL);
2696 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2697 g_option_context_parse (context, &argc, &argv, NULL);
2698 g_option_context_free(context);
2700 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2701 gboolean verbose_override = uzbl.state.verbose;
2703 /* initialize hash table */
2704 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2706 uzbl.net.soup_session = webkit_get_default_session();
2707 uzbl.state.keycmd = g_string_new("");
2709 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2710 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2711 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2712 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2713 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2714 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2717 if(uname(&uzbl.state.unameinfo) == -1)
2718 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2720 uzbl.gui.sbar.progress_s = g_strdup("=");
2721 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2722 uzbl.gui.sbar.progress_w = 10;
2724 /* HTML mode defaults*/
2725 uzbl.behave.html_buffer = g_string_new("");
2726 uzbl.behave.html_endmarker = g_strdup(".");
2727 uzbl.behave.html_timeout = 60;
2728 uzbl.behave.base_url = g_strdup("http://invalid");
2730 /* default mode indicators */
2731 uzbl.behave.insert_indicator = g_strdup("I");
2732 uzbl.behave.cmd_indicator = g_strdup("C");
2736 make_var_to_name_hash();
2738 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2740 uzbl.gui.scrolled_win = create_browser();
2743 /* initial packing */
2744 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2745 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2747 if (uzbl.state.socket_id) {
2748 uzbl.gui.plug = create_plug ();
2749 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2750 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2752 uzbl.gui.main_window = create_window ();
2753 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2754 gtk_widget_show_all (uzbl.gui.main_window);
2755 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2758 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2760 if (uzbl.state.verbose) {
2761 printf("Uzbl start location: %s\n", argv[0]);
2762 if (uzbl.state.socket_id)
2763 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2765 printf("window_id %i\n",(int) uzbl.xwin);
2766 printf("pid %i\n", getpid ());
2767 printf("name: %s\n", uzbl.state.instance_name);
2770 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2771 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2772 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2773 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2774 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2778 if (!uzbl.behave.show_status)
2779 gtk_widget_hide(uzbl.gui.mainbar);
2786 if (verbose_override > uzbl.state.verbose)
2787 uzbl.state.verbose = verbose_override;
2790 set_var_value("uri", uri_override);
2791 g_free(uri_override);
2792 } else if (uzbl.state.uri)
2793 cmd_load_uri(uzbl.gui.web_view, NULL);
2798 return EXIT_SUCCESS;
2802 /* vi: set et ts=4: */