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, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
644 if (uzbl.gui.main_title)
645 g_free (uzbl.gui.main_title);
646 uzbl.gui.main_title = g_strdup (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 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
811 set_var_value(g_strstrip(split[0]), value);
817 print(WebKitWebView *page, GArray *argv, GString *result) {
818 (void) page; (void) result;
821 buf = expand(argv_idx(argv, 0), 0);
822 g_string_assign(result, buf);
827 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
828 (void) page; (void) result;
829 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
830 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
831 add_binding(g_strstrip(split[0]), value);
843 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
844 (void) page; (void) result;
846 if (argv_idx(argv, 0)) {
847 if (strcmp (argv_idx(argv, 0), "0") == 0) {
848 uzbl.behave.insert_mode = FALSE;
850 uzbl.behave.insert_mode = TRUE;
853 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
860 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
863 if (argv_idx(argv, 0)) {
864 GString* newuri = g_string_new (argv_idx(argv, 0));
865 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
866 run_js(web_view, argv, NULL);
869 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
870 g_string_prepend (newuri, "http://");
871 /* if we do handle cookies, ask our handler for them */
872 webkit_web_view_load_uri (web_view, newuri->str);
873 g_string_free (newuri, TRUE);
880 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
881 size_t argumentCount, const JSValueRef arguments[],
882 JSValueRef* exception) {
887 JSStringRef js_result_string;
888 GString *result = g_string_new("");
890 if (argumentCount >= 1) {
891 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
892 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
893 char ctl_line[arg_size];
894 JSStringGetUTF8CString(arg, ctl_line, arg_size);
896 parse_cmd_line(ctl_line, result);
898 JSStringRelease(arg);
900 js_result_string = JSStringCreateWithUTF8CString(result->str);
902 g_string_free(result, TRUE);
904 return JSValueMakeString(ctx, js_result_string);
907 JSStaticFunction js_static_functions[] = {
908 {"run", js_run_command, kJSPropertyAttributeNone},
913 /* This function creates the class and its definition, only once */
914 if (!uzbl.js.initialized) {
915 /* it would be pretty cool to make this dynamic */
916 uzbl.js.classdef = kJSClassDefinitionEmpty;
917 uzbl.js.classdef.staticFunctions = js_static_functions;
919 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
925 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
926 WebKitWebFrame *frame;
927 JSGlobalContextRef context;
928 JSObjectRef globalobject;
929 JSStringRef var_name;
931 JSStringRef js_script;
932 JSValueRef js_result;
933 JSStringRef js_result_string;
934 size_t js_result_size;
938 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
939 context = webkit_web_frame_get_global_context(frame);
940 globalobject = JSContextGetGlobalObject(context);
942 /* uzbl javascript namespace */
943 var_name = JSStringCreateWithUTF8CString("Uzbl");
944 JSObjectSetProperty(context, globalobject, var_name,
945 JSObjectMake(context, uzbl.js.classref, NULL),
946 kJSClassAttributeNone, NULL);
948 /* evaluate the script and get return value*/
949 js_script = JSStringCreateWithUTF8CString(script);
950 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
951 if (js_result && !JSValueIsUndefined(context, js_result)) {
952 js_result_string = JSValueToStringCopy(context, js_result, NULL);
953 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
955 if (js_result_size) {
956 char js_result_utf8[js_result_size];
957 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
958 g_string_assign(result, js_result_utf8);
961 JSStringRelease(js_result_string);
965 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
967 JSStringRelease(var_name);
968 JSStringRelease(js_script);
972 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
973 if (argv_idx(argv, 0))
974 eval_js(web_view, argv_idx(argv, 0), result);
978 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
980 if (argv_idx(argv, 0)) {
981 GArray* lines = read_file_by_line (argv_idx (argv, 0));
986 while ((line = g_array_index(lines, gchar*, i))) {
988 js = g_strdup (line);
990 gchar* newjs = g_strconcat (js, line, NULL);
997 if (uzbl.state.verbose)
998 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1000 if (argv_idx (argv, 1)) {
1001 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1005 eval_js (web_view, js, result);
1007 g_array_free (lines, TRUE);
1012 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1013 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1014 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1015 webkit_web_view_unmark_text_matches (page);
1016 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1017 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1021 if (uzbl.state.searchtx) {
1022 if (uzbl.state.verbose)
1023 printf ("Searching: %s\n", uzbl.state.searchtx);
1024 webkit_web_view_set_highlight_text_matches (page, TRUE);
1025 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1030 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1032 search_text(page, argv, TRUE);
1036 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1038 search_text(page, argv, FALSE);
1042 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1043 (void) argv; (void) result;
1044 webkit_web_view_set_highlight_text_matches (page, FALSE);
1049 new_window_load_uri (const gchar * uri) {
1050 GString* to_execute = g_string_new ("");
1051 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1053 for (i = 0; entries[i].long_name != NULL; i++) {
1054 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1055 gchar** str = (gchar**)entries[i].arg_data;
1057 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1061 if (uzbl.state.verbose)
1062 printf("\n%s\n", to_execute->str);
1063 g_spawn_command_line_async (to_execute->str, NULL);
1064 g_string_free (to_execute, TRUE);
1068 chain (WebKitWebView *page, GArray *argv, GString *result) {
1069 (void) page; (void) result;
1071 gchar **parts = NULL;
1073 while ((a = argv_idx(argv, i++))) {
1074 parts = g_strsplit (a, " ", 2);
1076 parse_command(parts[0], parts[1], result);
1082 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1086 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1092 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1096 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1102 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1107 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1109 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1114 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1121 /* --Statusbar functions-- */
1123 build_progressbar_ascii(int percent) {
1124 int width=uzbl.gui.sbar.progress_w;
1127 GString *bar = g_string_new("");
1129 l = (double)percent*((double)width/100.);
1130 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1132 for(i=0; i<(int)l; i++)
1133 g_string_append(bar, uzbl.gui.sbar.progress_s);
1136 g_string_append(bar, uzbl.gui.sbar.progress_u);
1138 return g_string_free(bar, FALSE);
1143 const GScannerConfig scan_config = {
1146 ) /* cset_skip_characters */,
1151 ) /* cset_identifier_first */,
1158 ) /* cset_identifier_nth */,
1159 ( "" ) /* cpair_comment_single */,
1161 TRUE /* case_sensitive */,
1163 FALSE /* skip_comment_multi */,
1164 FALSE /* skip_comment_single */,
1165 FALSE /* scan_comment_multi */,
1166 TRUE /* scan_identifier */,
1167 TRUE /* scan_identifier_1char */,
1168 FALSE /* scan_identifier_NULL */,
1169 TRUE /* scan_symbols */,
1170 FALSE /* scan_binary */,
1171 FALSE /* scan_octal */,
1172 FALSE /* scan_float */,
1173 FALSE /* scan_hex */,
1174 FALSE /* scan_hex_dollar */,
1175 FALSE /* scan_string_sq */,
1176 FALSE /* scan_string_dq */,
1177 TRUE /* numbers_2_int */,
1178 FALSE /* int_2_float */,
1179 FALSE /* identifier_2_string */,
1180 FALSE /* char_2_token */,
1181 FALSE /* symbol_2_token */,
1182 TRUE /* scope_0_fallback */,
1187 uzbl.scan = g_scanner_new(&scan_config);
1188 while(symp->symbol_name) {
1189 g_scanner_scope_add_symbol(uzbl.scan, 0,
1191 GINT_TO_POINTER(symp->symbol_token));
1197 expand_template(const char *template, gboolean escape_markup) {
1198 if(!template) return NULL;
1200 GTokenType token = G_TOKEN_NONE;
1201 GString *ret = g_string_new("");
1205 g_scanner_input_text(uzbl.scan, template, strlen(template));
1206 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1207 token = g_scanner_get_next_token(uzbl.scan);
1209 if(token == G_TOKEN_SYMBOL) {
1210 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1214 buf = uzbl.state.uri ?
1215 g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1216 g_string_append(ret, buf);
1220 g_string_append(ret, uzbl.state.uri ?
1221 uzbl.state.uri : g_strdup(""));
1224 buf = itos(uzbl.gui.sbar.load_progress);
1225 g_string_append(ret, buf);
1228 case SYM_LOADPRGSBAR:
1229 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1230 g_string_append(ret, buf);
1235 buf = uzbl.gui.main_title ?
1236 g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1237 g_string_append(ret, buf);
1241 g_string_append(ret, uzbl.gui.main_title ?
1242 uzbl.gui.main_title : "");
1244 case SYM_SELECTED_URI:
1246 buf = uzbl.state.selected_url ?
1247 g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1248 g_string_append(ret, buf);
1252 g_string_append(ret, uzbl.state.selected_url ?
1253 uzbl.state.selected_url : "");
1256 buf = itos(uzbl.xwin);
1257 g_string_append(ret,
1258 uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1263 buf = uzbl.state.keycmd->str ?
1264 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1265 g_string_append(ret, buf);
1269 g_string_append(ret, uzbl.state.keycmd->str ?
1270 uzbl.state.keycmd->str : "");
1273 g_string_append(ret,
1274 uzbl.behave.insert_mode ?
1275 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1278 g_string_append(ret,
1279 uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1281 /* useragent syms */
1283 buf = itos(WEBKIT_MAJOR_VERSION);
1284 g_string_append(ret, buf);
1288 buf = itos(WEBKIT_MINOR_VERSION);
1289 g_string_append(ret, buf);
1293 buf = itos(WEBKIT_MICRO_VERSION);
1294 g_string_append(ret, buf);
1298 g_string_append(ret, uzbl.state.unameinfo.sysname);
1301 g_string_append(ret, uzbl.state.unameinfo.nodename);
1304 g_string_append(ret, uzbl.state.unameinfo.release);
1307 g_string_append(ret, uzbl.state.unameinfo.version);
1310 g_string_append(ret, uzbl.state.unameinfo.machine);
1313 g_string_append(ret, ARCH);
1316 case SYM_DOMAINNAME:
1317 g_string_append(ret, uzbl.state.unameinfo.domainname);
1321 g_string_append(ret, COMMIT);
1327 else if(token == G_TOKEN_INT) {
1328 g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1330 else if(token == G_TOKEN_IDENTIFIER) {
1331 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1333 else if(token == G_TOKEN_CHAR) {
1334 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1336 else if(token == G_TOKEN_ERROR) {
1337 g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.",
1339 g_scanner_cur_line(uzbl.scan),
1340 g_scanner_cur_position(uzbl.scan));
1344 return g_string_free(ret, FALSE);
1346 /* --End Statusbar functions-- */
1349 sharg_append(GArray *a, const gchar *str) {
1350 const gchar *s = (str ? str : "");
1351 g_array_append_val(a, s);
1354 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1356 run_command (const gchar *command, const guint npre, const gchar **args,
1357 const gboolean sync, char **output_stdout) {
1358 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1361 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1362 gchar *pid = itos(getpid());
1363 gchar *xwin = itos(uzbl.xwin);
1365 sharg_append(a, command);
1366 for (i = 0; i < npre; i++) /* add n args before the default vars */
1367 sharg_append(a, args[i]);
1368 sharg_append(a, uzbl.state.config_file);
1369 sharg_append(a, pid);
1370 sharg_append(a, xwin);
1371 sharg_append(a, uzbl.comm.fifo_path);
1372 sharg_append(a, uzbl.comm.socket_path);
1373 sharg_append(a, uzbl.state.uri);
1374 sharg_append(a, uzbl.gui.main_title);
1376 for (i = npre; i < g_strv_length((gchar**)args); i++)
1377 sharg_append(a, args[i]);
1381 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1383 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1384 NULL, NULL, output_stdout, NULL, NULL, &err);
1385 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1386 NULL, NULL, NULL, &err);
1388 if (uzbl.state.verbose) {
1389 GString *s = g_string_new("spawned:");
1390 for (i = 0; i < (a->len); i++) {
1391 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1392 g_string_append_printf(s, " %s", qarg);
1395 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1396 printf("%s\n", s->str);
1397 g_string_free(s, TRUE);
1399 printf("Stdout: %s\n", *output_stdout);
1403 g_printerr("error on run_command: %s\n", err->message);
1408 g_array_free (a, TRUE);
1413 split_quoted(const gchar* src, const gboolean unquote) {
1414 /* split on unquoted space, return array of strings;
1415 remove a layer of quotes and backslashes if unquote */
1416 if (!src) return NULL;
1418 gboolean dq = FALSE;
1419 gboolean sq = FALSE;
1420 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1421 GString *s = g_string_new ("");
1425 for (p = src; *p != '\0'; p++) {
1426 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1427 else if (*p == '\\') { g_string_append_c(s, *p++);
1428 g_string_append_c(s, *p); }
1429 else if ((*p == '"') && unquote && !sq) dq = !dq;
1430 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1432 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1433 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1435 else if ((*p == ' ') && !dq && !sq) {
1436 dup = g_strdup(s->str);
1437 g_array_append_val(a, dup);
1438 g_string_truncate(s, 0);
1439 } else g_string_append_c(s, *p);
1441 dup = g_strdup(s->str);
1442 g_array_append_val(a, dup);
1443 ret = (gchar**)a->data;
1444 g_array_free (a, FALSE);
1445 g_string_free (s, TRUE);
1450 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1451 (void)web_view; (void)result;
1452 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1453 if (argv_idx(argv, 0))
1454 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1458 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1459 (void)web_view; (void)result;
1461 if (argv_idx(argv, 0))
1462 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1463 TRUE, &uzbl.comm.sync_stdout);
1467 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1468 (void)web_view; (void)result;
1469 if (!uzbl.behave.shell_cmd) {
1470 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1475 gchar *spacer = g_strdup("");
1476 g_array_insert_val(argv, 1, spacer);
1477 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1479 for (i = 1; i < g_strv_length(cmd); i++)
1480 g_array_prepend_val(argv, cmd[i]);
1482 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1488 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1489 (void)web_view; (void)result;
1490 if (!uzbl.behave.shell_cmd) {
1491 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1496 gchar *spacer = g_strdup("");
1497 g_array_insert_val(argv, 1, spacer);
1498 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1500 for (i = 1; i < g_strv_length(cmd); i++)
1501 g_array_prepend_val(argv, cmd[i]);
1503 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1504 TRUE, &uzbl.comm.sync_stdout);
1510 parse_command(const char *cmd, const char *param, GString *result) {
1513 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1515 gchar **par = split_quoted(param, TRUE);
1516 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1518 if (c->no_split) { /* don't split */
1519 sharg_append(a, param);
1521 for (i = 0; i < g_strv_length(par); i++)
1522 sharg_append(a, par[i]);
1525 if (result == NULL) {
1526 GString *result_print = g_string_new("");
1528 c->function(uzbl.gui.web_view, a, result_print);
1529 if (result_print->len)
1530 printf("%*s\n", result_print->len, result_print->str);
1532 g_string_free(result_print, TRUE);
1534 c->function(uzbl.gui.web_view, a, result);
1537 g_array_free (a, TRUE);
1540 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1547 if(*uzbl.net.proxy_url == ' '
1548 || uzbl.net.proxy_url == NULL) {
1549 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1550 (GType) SOUP_SESSION_PROXY_URI);
1553 suri = soup_uri_new(uzbl.net.proxy_url);
1554 g_object_set(G_OBJECT(uzbl.net.soup_session),
1555 SOUP_SESSION_PROXY_URI,
1557 soup_uri_free(suri);
1564 if(file_exists(uzbl.gui.icon)) {
1565 if (uzbl.gui.main_window)
1566 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1568 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1574 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1575 g_array_append_val (a, uzbl.state.uri);
1576 load_uri(uzbl.gui.web_view, a, NULL);
1577 g_array_free (a, TRUE);
1581 cmd_always_insert_mode() {
1582 uzbl.behave.insert_mode =
1583 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1589 g_object_set(G_OBJECT(uzbl.net.soup_session),
1590 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1594 cmd_max_conns_host() {
1595 g_object_set(G_OBJECT(uzbl.net.soup_session),
1596 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1601 soup_session_remove_feature
1602 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1603 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1604 /*g_free(uzbl.net.soup_logger);*/
1606 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1607 soup_session_add_feature(uzbl.net.soup_session,
1608 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1613 return webkit_web_view_get_settings(uzbl.gui.web_view);
1618 WebKitWebSettings *ws = view_settings();
1619 if (uzbl.behave.font_size > 0) {
1620 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1623 if (uzbl.behave.monospace_size > 0) {
1624 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1625 uzbl.behave.monospace_size, NULL);
1627 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1628 uzbl.behave.font_size, NULL);
1634 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1638 cmd_disable_plugins() {
1639 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1640 !uzbl.behave.disable_plugins, NULL);
1644 cmd_disable_scripts() {
1645 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1646 !uzbl.behave.disable_scripts, NULL);
1650 cmd_minimum_font_size() {
1651 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1652 uzbl.behave.minimum_font_size, NULL);
1655 cmd_autoload_img() {
1656 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1657 uzbl.behave.autoload_img, NULL);
1662 cmd_autoshrink_img() {
1663 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1664 uzbl.behave.autoshrink_img, NULL);
1669 cmd_enable_spellcheck() {
1670 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1671 uzbl.behave.enable_spellcheck, NULL);
1675 cmd_enable_private() {
1676 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1677 uzbl.behave.enable_private, NULL);
1682 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1683 uzbl.behave.print_bg, NULL);
1688 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1689 uzbl.behave.style_uri, NULL);
1693 cmd_resizable_txt() {
1694 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1695 uzbl.behave.resizable_txt, NULL);
1699 cmd_default_encoding() {
1700 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1701 uzbl.behave.default_encoding, NULL);
1705 cmd_enforce_96dpi() {
1706 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1707 uzbl.behave.enforce_96dpi, NULL);
1711 cmd_caret_browsing() {
1712 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1713 uzbl.behave.caret_browsing, NULL);
1717 cmd_cookie_handler() {
1718 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1719 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1720 if ((g_strcmp0(split[0], "sh") == 0) ||
1721 (g_strcmp0(split[0], "spawn") == 0)) {
1722 g_free (uzbl.behave.cookie_handler);
1723 uzbl.behave.cookie_handler =
1724 g_strdup_printf("sync_%s %s", split[0], split[1]);
1731 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1736 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1741 if(uzbl.behave.inject_html) {
1742 webkit_web_view_load_html_string (uzbl.gui.web_view,
1743 uzbl.behave.inject_html, NULL);
1752 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1753 uzbl.behave.modmask = 0;
1755 if(uzbl.behave.modkey)
1756 g_free(uzbl.behave.modkey);
1757 uzbl.behave.modkey = buf;
1759 for (i = 0; modkeys[i].key != NULL; i++) {
1760 if (g_strrstr(buf, modkeys[i].key))
1761 uzbl.behave.modmask |= modkeys[i].mask;
1767 if (*uzbl.net.useragent == ' ') {
1768 g_free (uzbl.net.useragent);
1769 uzbl.net.useragent = NULL;
1771 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1773 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1774 g_free(uzbl.net.useragent);
1775 uzbl.net.useragent = ua;
1781 gtk_widget_ref(uzbl.gui.scrolled_win);
1782 gtk_widget_ref(uzbl.gui.mainbar);
1783 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1784 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1786 if(uzbl.behave.status_top) {
1787 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1788 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1791 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1792 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1794 gtk_widget_unref(uzbl.gui.scrolled_win);
1795 gtk_widget_unref(uzbl.gui.mainbar);
1796 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1801 set_var_value(gchar *name, gchar *val) {
1802 uzbl_cmdprop *c = NULL;
1806 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1807 /* check for the variable type */
1808 if (c->type == TYPE_STR) {
1809 buf = expand(val, 0);
1812 } else if(c->type == TYPE_INT) {
1813 int *ip = (int *)c->ptr;
1814 buf = expand(val, 0);
1815 *ip = (int)strtoul(buf, &endp, 10);
1817 } else if (c->type == TYPE_FLOAT) {
1818 float *fp = (float *)c->ptr;
1819 buf = expand(val, 0);
1820 *fp = strtod(buf, &endp);
1824 /* invoke a command specific function */
1825 if(c->func) c->func();
1832 Behaviour *b = &uzbl.behave;
1834 if(b->html_buffer->str) {
1835 webkit_web_view_load_html_string (uzbl.gui.web_view,
1836 b->html_buffer->str, b->base_url);
1837 g_string_free(b->html_buffer, TRUE);
1838 b->html_buffer = g_string_new("");
1842 enum {M_CMD, M_HTML};
1844 parse_cmd_line(const char *ctl_line, GString *result) {
1845 Behaviour *b = &uzbl.behave;
1848 if(b->mode == M_HTML) {
1849 len = strlen(b->html_endmarker);
1850 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1851 if(len == strlen(ctl_line)-1 &&
1852 !strncmp(b->html_endmarker, ctl_line, len)) {
1854 set_var_value("mode", "0");
1859 set_timeout(b->html_timeout);
1860 g_string_append(b->html_buffer, ctl_line);
1863 else if((ctl_line[0] == '#') /* Comments */
1864 || (ctl_line[0] == ' ')
1865 || (ctl_line[0] == '\n'))
1866 ; /* ignore these lines */
1867 else { /* parse a command */
1869 gchar **tokens = NULL;
1870 len = strlen(ctl_line);
1872 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1873 ctlstrip = g_strndup(ctl_line, len - 1);
1874 else ctlstrip = g_strdup(ctl_line);
1876 tokens = g_strsplit(ctlstrip, " ", 2);
1877 parse_command(tokens[0], tokens[1], result);
1884 build_stream_name(int type, const gchar* dir) {
1885 char *xwin_str = NULL;
1886 State *s = &uzbl.state;
1889 xwin_str = itos((int)uzbl.xwin);
1891 str = g_strdup_printf
1892 ("%s/uzbl_fifo_%s", dir,
1893 s->instance_name ? s->instance_name : xwin_str);
1894 } else if (type == SOCKET) {
1895 str = g_strdup_printf
1896 ("%s/uzbl_socket_%s", dir,
1897 s->instance_name ? s->instance_name : xwin_str );
1904 control_fifo(GIOChannel *gio, GIOCondition condition) {
1905 if (uzbl.state.verbose)
1906 printf("triggered\n");
1911 if (condition & G_IO_HUP)
1912 g_error ("Fifo: Read end of pipe died!\n");
1915 g_error ("Fifo: GIOChannel broke\n");
1917 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1918 if (ret == G_IO_STATUS_ERROR) {
1919 g_error ("Fifo: Error reading: %s\n", err->message);
1923 parse_cmd_line(ctl_line, NULL);
1930 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1931 GIOChannel *chan = NULL;
1932 GError *error = NULL;
1933 gchar *path = build_stream_name(FIFO, dir);
1935 if (!file_exists(path)) {
1936 if (mkfifo (path, 0666) == 0) {
1937 // 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.
1938 chan = g_io_channel_new_file(path, "r+", &error);
1940 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1941 if (uzbl.state.verbose)
1942 printf ("init_fifo: created successfully as %s\n", path);
1943 uzbl.comm.fifo_path = path;
1945 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1946 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1947 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1948 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1950 /* if we got this far, there was an error; cleanup */
1951 if (error) g_error_free (error);
1958 control_stdin(GIOChannel *gio, GIOCondition condition) {
1960 gchar *ctl_line = NULL;
1963 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1964 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1967 parse_cmd_line(ctl_line, NULL);
1975 GIOChannel *chan = NULL;
1976 GError *error = NULL;
1978 chan = g_io_channel_unix_new(fileno(stdin));
1980 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1981 g_error ("Stdin: could not add watch\n");
1983 if (uzbl.state.verbose)
1984 printf ("Stdin: watch added successfully\n");
1987 g_error ("Stdin: Error while opening: %s\n", error->message);
1989 if (error) g_error_free (error);
1993 control_socket(GIOChannel *chan) {
1994 struct sockaddr_un remote;
1995 unsigned int t = sizeof(remote);
1997 GIOChannel *clientchan;
1999 clientsock = accept (g_io_channel_unix_get_fd(chan),
2000 (struct sockaddr *) &remote, &t);
2002 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2003 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2004 (GIOFunc) control_client_socket, clientchan);
2011 control_client_socket(GIOChannel *clientchan) {
2013 GString *result = g_string_new("");
2014 GError *error = NULL;
2018 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2019 if (ret == G_IO_STATUS_ERROR) {
2020 g_warning ("Error reading: %s\n", error->message);
2021 g_io_channel_shutdown(clientchan, TRUE, &error);
2023 } else if (ret == G_IO_STATUS_EOF) {
2024 /* shutdown and remove channel watch from main loop */
2025 g_io_channel_shutdown(clientchan, TRUE, &error);
2030 parse_cmd_line (ctl_line, result);
2031 g_string_append_c(result, '\n');
2032 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2034 if (ret == G_IO_STATUS_ERROR) {
2035 g_warning ("Error writing: %s", error->message);
2037 g_io_channel_flush(clientchan, &error);
2040 if (error) g_error_free (error);
2041 g_string_free(result, TRUE);
2047 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2048 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2049 if (unlink(uzbl.comm.socket_path) == -1)
2050 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2051 g_free(uzbl.comm.socket_path);
2052 uzbl.comm.socket_path = NULL;
2060 GIOChannel *chan = NULL;
2062 struct sockaddr_un local;
2063 gchar *path = build_stream_name(SOCKET, dir);
2065 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2067 local.sun_family = AF_UNIX;
2068 strcpy (local.sun_path, path);
2069 unlink (local.sun_path);
2071 len = strlen (local.sun_path) + sizeof (local.sun_family);
2072 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2073 if (uzbl.state.verbose)
2074 printf ("init_socket: opened in %s\n", path);
2077 if( (chan = g_io_channel_unix_new(sock)) ) {
2078 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2079 uzbl.comm.socket_path = path;
2082 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2084 /* if we got this far, there was an error; cleanup */
2091 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2092 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2094 // this function may be called very early when the templates are not set (yet), hence the checks
2096 update_title (void) {
2097 Behaviour *b = &uzbl.behave;
2100 if (b->show_status) {
2101 if (b->title_format_short) {
2102 parsed = expand_template(b->title_format_short, FALSE);
2103 if (uzbl.gui.main_window)
2104 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2107 if (b->status_format) {
2108 parsed = expand_template(b->status_format, TRUE);
2109 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2112 if (b->status_background) {
2114 gdk_color_parse (b->status_background, &color);
2115 //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)
2116 if (uzbl.gui.main_window)
2117 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2120 if (b->title_format_long) {
2121 parsed = expand_template(b->title_format_long, FALSE);
2122 if (uzbl.gui.main_window)
2123 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2130 key_press_cb (GtkWidget* window, GdkEventKey* event)
2132 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2136 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2137 || 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)
2140 /* turn off insert mode (if always_insert_mode is not used) */
2141 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2142 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2147 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2150 if (event->keyval == GDK_Escape) {
2151 g_string_truncate(uzbl.state.keycmd, 0);
2153 dehilight(uzbl.gui.web_view, NULL, NULL);
2157 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2158 if (event->keyval == GDK_Insert) {
2160 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2161 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2163 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2166 g_string_append (uzbl.state.keycmd, str);
2173 if (event->keyval == GDK_BackSpace)
2174 keycmd_bs(NULL, NULL, NULL);
2176 gboolean key_ret = FALSE;
2177 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2179 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2181 run_keycmd(key_ret);
2183 if (key_ret) return (!uzbl.behave.insert_mode);
2188 run_keycmd(const gboolean key_ret) {
2189 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2191 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2192 g_string_truncate(uzbl.state.keycmd, 0);
2193 parse_command(act->name, act->param, NULL);
2197 /* try if it's an incremental keycmd or one that takes args, and run it */
2198 GString* short_keys = g_string_new ("");
2199 GString* short_keys_inc = g_string_new ("");
2201 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2202 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2203 g_string_assign(short_keys_inc, short_keys->str);
2204 g_string_append_c(short_keys, '_');
2205 g_string_append_c(short_keys_inc, '*');
2207 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2208 /* run normal cmds only if return was pressed */
2209 exec_paramcmd(act, i);
2210 g_string_truncate(uzbl.state.keycmd, 0);
2212 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2213 if (key_ret) /* just quit the incremental command on return */
2214 g_string_truncate(uzbl.state.keycmd, 0);
2215 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2219 g_string_truncate(short_keys, short_keys->len - 1);
2221 g_string_free (short_keys, TRUE);
2222 g_string_free (short_keys_inc, TRUE);
2226 exec_paramcmd(const Action *act, const guint i) {
2227 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2228 GString *actionname = g_string_new ("");
2229 GString *actionparam = g_string_new ("");
2230 g_string_erase (parampart, 0, i+1);
2232 g_string_printf (actionname, act->name, parampart->str);
2234 g_string_printf (actionparam, act->param, parampart->str);
2235 parse_command(actionname->str, actionparam->str, NULL);
2236 g_string_free(actionname, TRUE);
2237 g_string_free(actionparam, TRUE);
2238 g_string_free(parampart, TRUE);
2246 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2247 //main_window_ref = g_object_ref(scrolled_window);
2248 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
2250 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2251 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2253 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2254 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2255 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2256 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2257 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2258 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2259 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2265 return scrolled_window;
2272 g->mainbar = gtk_hbox_new (FALSE, 0);
2274 /* keep a reference to the bar so we can re-pack it at runtime*/
2275 //sbar_ref = g_object_ref(g->mainbar);
2277 g->mainbar_label = gtk_label_new ("");
2278 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2279 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2280 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2281 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2282 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2283 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2289 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2290 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2291 gtk_widget_set_name (window, "Uzbl browser");
2292 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2293 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2300 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2301 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2302 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2309 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2311 If actname is one that calls an external command, this function will inject
2312 newargs in front of the user-provided args in that command line. They will
2313 come become after the body of the script (in sh) or after the name of
2314 the command to execute (in spawn).
2315 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2316 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2318 The return value consist of two strings: the action (sh, ...) and its args.
2320 If act is not one that calls an external command, then the given action merely
2323 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2324 /* Arrr! Here be memory leaks */
2325 gchar *actdup = g_strdup(actname);
2326 g_array_append_val(rets, actdup);
2328 if ((g_strcmp0(actname, "spawn") == 0) ||
2329 (g_strcmp0(actname, "sh") == 0) ||
2330 (g_strcmp0(actname, "sync_spawn") == 0) ||
2331 (g_strcmp0(actname, "sync_sh") == 0)) {
2333 GString *a = g_string_new("");
2334 gchar **spawnparts = split_quoted(origargs, FALSE);
2335 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2336 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2338 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2339 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2341 g_array_append_val(rets, a->str);
2342 g_string_free(a, FALSE);
2343 g_strfreev(spawnparts);
2345 gchar *origdup = g_strdup(origargs);
2346 g_array_append_val(rets, origdup);
2348 return (gchar**)g_array_free(rets, FALSE);
2352 run_handler (const gchar *act, const gchar *args) {
2353 /* Consider this code a temporary hack to make the handlers usable.
2354 In practice, all this splicing, injection, and reconstruction is
2355 inefficient, annoying and hard to manage. Potential pitfalls arise
2356 when the handler specific args 1) are not quoted (the handler
2357 callbacks should take care of this) 2) are quoted but interfere
2358 with the users' own quotation. A more ideal solution is
2359 to refactor parse_command so that it doesn't just take a string
2360 and execute it; rather than that, we should have a function which
2361 returns the argument vector parsed from the string. This vector
2362 could be modified (e.g. insert additional args into it) before
2363 passing it to the next function that actually executes it. Though
2364 it still isn't perfect for chain actions.. will reconsider & re-
2365 factor when I have the time. -duc */
2367 char **parts = g_strsplit(act, " ", 2);
2369 if (g_strcmp0(parts[0], "chain") == 0) {
2370 GString *newargs = g_string_new("");
2371 gchar **chainparts = split_quoted(parts[1], FALSE);
2373 /* for every argument in the chain, inject the handler args
2374 and make sure the new parts are wrapped in quotes */
2375 gchar **cp = chainparts;
2377 gchar *quotless = NULL;
2378 gchar **spliced_quotless = NULL; // sigh -_-;
2379 gchar **inpart = NULL;
2382 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2384 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2385 } else quotless = g_strdup(*cp);
2387 spliced_quotless = g_strsplit(quotless, " ", 2);
2388 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2389 g_strfreev(spliced_quotless);
2391 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2397 parse_command(parts[0], &(newargs->str[1]), NULL);
2398 g_string_free(newargs, TRUE);
2399 g_strfreev(chainparts);
2402 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2403 parse_command(inparts[0], inparts[1], NULL);
2411 add_binding (const gchar *key, const gchar *act) {
2412 char **parts = g_strsplit(act, " ", 2);
2419 if (uzbl.state.verbose)
2420 printf ("Binding %-10s : %s\n", key, act);
2421 action = new_action(parts[0], parts[1]);
2423 if (g_hash_table_remove (uzbl.bindings, key))
2424 g_warning ("Overwriting existing binding for \"%s\"", key);
2425 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2430 get_xdg_var (XDG_Var xdg) {
2431 const gchar* actual_value = getenv (xdg.environmental);
2432 const gchar* home = getenv ("HOME");
2433 gchar* return_value;
2435 if (! actual_value || strcmp (actual_value, "") == 0) {
2436 if (xdg.default_value) {
2437 return_value = str_replace ("~", home, xdg.default_value);
2439 return_value = NULL;
2442 return_value = str_replace("~", home, actual_value);
2445 return return_value;
2449 find_xdg_file (int xdg_type, char* filename) {
2450 /* xdg_type = 0 => config
2451 xdg_type = 1 => data
2452 xdg_type = 2 => cache*/
2454 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2455 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2458 gchar* temporary_string;
2462 if (! file_exists (temporary_file) && xdg_type != 2) {
2463 buf = get_xdg_var (XDG[3 + xdg_type]);
2464 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2467 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2468 g_free (temporary_file);
2469 temporary_file = g_strconcat (temporary_string, filename, NULL);
2473 //g_free (temporary_string); - segfaults.
2475 if (file_exists (temporary_file)) {
2476 return temporary_file;
2483 State *s = &uzbl.state;
2484 Network *n = &uzbl.net;
2486 for (i = 0; default_config[i].command != NULL; i++) {
2487 parse_cmd_line(default_config[i].command, NULL);
2490 if (!s->config_file) {
2491 s->config_file = find_xdg_file (0, "/uzbl/config");
2494 if (s->config_file) {
2495 GArray* lines = read_file_by_line (s->config_file);
2499 while ((line = g_array_index(lines, gchar*, i))) {
2500 parse_cmd_line (line, NULL);
2504 g_array_free (lines, TRUE);
2506 if (uzbl.state.verbose)
2507 printf ("No configuration file loaded.\n");
2510 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2513 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2516 if (!uzbl.behave.cookie_handler)
2519 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2520 GString *s = g_string_new ("");
2521 SoupURI * soup_uri = soup_message_get_uri(msg);
2522 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2523 run_handler(uzbl.behave.cookie_handler, s->str);
2525 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2526 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2527 if ( p != NULL ) *p = '\0';
2528 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2530 if (uzbl.comm.sync_stdout)
2531 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2533 g_string_free(s, TRUE);
2537 save_cookies (SoupMessage *msg, gpointer user_data){
2541 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2542 cookie = soup_cookie_to_set_cookie_header(ck->data);
2543 SoupURI * soup_uri = soup_message_get_uri(msg);
2544 GString *s = g_string_new ("");
2545 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2546 run_handler(uzbl.behave.cookie_handler, s->str);
2548 g_string_free(s, TRUE);
2553 /* --- WEBINSPECTOR --- */
2555 hide_window_cb(GtkWidget *widget, gpointer data) {
2558 gtk_widget_hide(widget);
2562 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2565 (void) web_inspector;
2566 GtkWidget* scrolled_window;
2567 GtkWidget* new_web_view;
2570 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2571 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2572 G_CALLBACK(hide_window_cb), NULL);
2574 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2575 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2576 gtk_widget_show(g->inspector_window);
2578 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2579 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2580 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2581 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2582 gtk_widget_show(scrolled_window);
2584 new_web_view = webkit_web_view_new();
2585 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2587 return WEBKIT_WEB_VIEW(new_web_view);
2591 inspector_show_window_cb (WebKitWebInspector* inspector){
2593 gtk_widget_show(uzbl.gui.inspector_window);
2597 /* TODO: Add variables and code to make use of these functions */
2599 inspector_close_window_cb (WebKitWebInspector* inspector){
2605 inspector_attach_window_cb (WebKitWebInspector* inspector){
2611 inspector_detach_window_cb (WebKitWebInspector* inspector){
2617 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2623 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2629 set_up_inspector() {
2631 WebKitWebSettings *settings = view_settings();
2632 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2634 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2635 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2636 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2637 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2638 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2639 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2640 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2642 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2646 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2648 uzbl_cmdprop *c = v;
2653 if(c->type == TYPE_STR)
2654 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2655 else if(c->type == TYPE_INT)
2656 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2657 else if(c->type == TYPE_FLOAT)
2658 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2662 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2666 printf("bind %s = %s %s\n", (char *)k ,
2667 (char *)a->name, a->param?(char *)a->param:"");
2672 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2673 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2676 #ifndef UZBL_LIBRARY
2679 main (int argc, char* argv[]) {
2680 gtk_init (&argc, &argv);
2681 if (!g_thread_supported ())
2682 g_thread_init (NULL);
2683 uzbl.state.executable_path = g_strdup(argv[0]);
2684 uzbl.state.selected_url = NULL;
2685 uzbl.state.searchtx = NULL;
2687 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2688 g_option_context_add_main_entries (context, entries, NULL);
2689 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2690 g_option_context_parse (context, &argc, &argv, NULL);
2691 g_option_context_free(context);
2693 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2694 gboolean verbose_override = uzbl.state.verbose;
2696 /* initialize hash table */
2697 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2699 uzbl.net.soup_session = webkit_get_default_session();
2700 uzbl.state.keycmd = g_string_new("");
2702 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2703 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2704 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2705 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2706 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2707 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2710 if(uname(&uzbl.state.unameinfo) == -1)
2711 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2713 uzbl.gui.sbar.progress_s = g_strdup("=");
2714 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2715 uzbl.gui.sbar.progress_w = 10;
2717 /* HTML mode defaults*/
2718 uzbl.behave.html_buffer = g_string_new("");
2719 uzbl.behave.html_endmarker = g_strdup(".");
2720 uzbl.behave.html_timeout = 60;
2721 uzbl.behave.base_url = g_strdup("http://invalid");
2723 /* default mode indicators */
2724 uzbl.behave.insert_indicator = g_strdup("I");
2725 uzbl.behave.cmd_indicator = g_strdup("C");
2729 make_var_to_name_hash();
2731 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2733 uzbl.gui.scrolled_win = create_browser();
2736 /* initial packing */
2737 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2738 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2740 if (uzbl.state.socket_id) {
2741 uzbl.gui.plug = create_plug ();
2742 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2743 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2745 uzbl.gui.main_window = create_window ();
2746 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2747 gtk_widget_show_all (uzbl.gui.main_window);
2748 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2751 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2753 if (uzbl.state.verbose) {
2754 printf("Uzbl start location: %s\n", argv[0]);
2755 if (uzbl.state.socket_id)
2756 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2758 printf("window_id %i\n",(int) uzbl.xwin);
2759 printf("pid %i\n", getpid ());
2760 printf("name: %s\n", uzbl.state.instance_name);
2763 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2764 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2765 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2766 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2767 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2771 if (!uzbl.behave.show_status)
2772 gtk_widget_hide(uzbl.gui.mainbar);
2781 if (verbose_override > uzbl.state.verbose)
2782 uzbl.state.verbose = verbose_override;
2785 set_var_value("uri", uri_override);
2786 g_free(uri_override);
2787 } else if (uzbl.state.uri)
2788 cmd_load_uri(uzbl.gui.web_view, NULL);
2793 return EXIT_SUCCESS;
2797 /* vi: set et ts=4: */