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) static 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 static 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 static 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);
1075 parse_command(parts[0], parts[1], result);
1081 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1085 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1091 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1095 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1101 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1106 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1108 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1113 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1120 /* --Statusbar functions-- */
1122 build_progressbar_ascii(int percent) {
1123 int width=uzbl.gui.sbar.progress_w;
1126 GString *bar = g_string_new("");
1128 l = (double)percent*((double)width/100.);
1129 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1131 for(i=0; i<(int)l; i++)
1132 g_string_append(bar, uzbl.gui.sbar.progress_s);
1135 g_string_append(bar, uzbl.gui.sbar.progress_u);
1137 return g_string_free(bar, FALSE);
1142 const GScannerConfig scan_config = {
1145 ) /* cset_skip_characters */,
1150 ) /* cset_identifier_first */,
1157 ) /* cset_identifier_nth */,
1158 ( "" ) /* cpair_comment_single */,
1160 TRUE /* case_sensitive */,
1162 FALSE /* skip_comment_multi */,
1163 FALSE /* skip_comment_single */,
1164 FALSE /* scan_comment_multi */,
1165 TRUE /* scan_identifier */,
1166 TRUE /* scan_identifier_1char */,
1167 FALSE /* scan_identifier_NULL */,
1168 TRUE /* scan_symbols */,
1169 FALSE /* scan_binary */,
1170 FALSE /* scan_octal */,
1171 FALSE /* scan_float */,
1172 FALSE /* scan_hex */,
1173 FALSE /* scan_hex_dollar */,
1174 FALSE /* scan_string_sq */,
1175 FALSE /* scan_string_dq */,
1176 TRUE /* numbers_2_int */,
1177 FALSE /* int_2_float */,
1178 FALSE /* identifier_2_string */,
1179 FALSE /* char_2_token */,
1180 FALSE /* symbol_2_token */,
1181 TRUE /* scope_0_fallback */,
1186 uzbl.scan = g_scanner_new(&scan_config);
1187 while(symp->symbol_name) {
1188 g_scanner_scope_add_symbol(uzbl.scan, 0,
1190 GINT_TO_POINTER(symp->symbol_token));
1196 expand_template(const char *template, gboolean escape_markup) {
1197 if(!template) return NULL;
1199 GTokenType token = G_TOKEN_NONE;
1200 GString *ret = g_string_new("");
1204 g_scanner_input_text(uzbl.scan, template, strlen(template));
1205 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1206 token = g_scanner_get_next_token(uzbl.scan);
1208 if(token == G_TOKEN_SYMBOL) {
1209 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1213 buf = uzbl.state.uri ?
1214 g_markup_printf_escaped("%s", uzbl.state.uri) : g_strdup("");
1215 g_string_append(ret, buf);
1219 g_string_append(ret, uzbl.state.uri ?
1220 uzbl.state.uri : g_strdup(""));
1223 buf = itos(uzbl.gui.sbar.load_progress);
1224 g_string_append(ret, buf);
1227 case SYM_LOADPRGSBAR:
1228 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1229 g_string_append(ret, buf);
1234 buf = uzbl.gui.main_title ?
1235 g_markup_printf_escaped("%s", uzbl.gui.main_title) : g_strdup("");
1236 g_string_append(ret, buf);
1240 g_string_append(ret, uzbl.gui.main_title ?
1241 uzbl.gui.main_title : "");
1243 case SYM_SELECTED_URI:
1245 buf = uzbl.state.selected_url ?
1246 g_markup_printf_escaped("%s", uzbl.state.selected_url) : g_strdup("");
1247 g_string_append(ret, buf);
1251 g_string_append(ret, uzbl.state.selected_url ?
1252 uzbl.state.selected_url : "");
1255 buf = itos(uzbl.xwin);
1256 g_string_append(ret,
1257 uzbl.state.instance_name ? uzbl.state.instance_name : buf);
1262 buf = uzbl.state.keycmd->str ?
1263 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) : g_strdup("");
1264 g_string_append(ret, buf);
1268 g_string_append(ret, uzbl.state.keycmd->str ?
1269 uzbl.state.keycmd->str : "");
1272 g_string_append(ret,
1273 uzbl.behave.insert_mode ?
1274 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
1277 g_string_append(ret,
1278 uzbl.gui.sbar.msg ? uzbl.gui.sbar.msg : "");
1280 /* useragent syms */
1282 buf = itos(WEBKIT_MAJOR_VERSION);
1283 g_string_append(ret, buf);
1287 buf = itos(WEBKIT_MINOR_VERSION);
1288 g_string_append(ret, buf);
1292 buf = itos(WEBKIT_MICRO_VERSION);
1293 g_string_append(ret, buf);
1297 g_string_append(ret, uzbl.state.unameinfo.sysname);
1300 g_string_append(ret, uzbl.state.unameinfo.nodename);
1303 g_string_append(ret, uzbl.state.unameinfo.release);
1306 g_string_append(ret, uzbl.state.unameinfo.version);
1309 g_string_append(ret, uzbl.state.unameinfo.machine);
1312 g_string_append(ret, ARCH);
1315 case SYM_DOMAINNAME:
1316 g_string_append(ret, uzbl.state.unameinfo.domainname);
1320 g_string_append(ret, COMMIT);
1326 else if(token == G_TOKEN_INT) {
1327 g_string_append_printf(ret, "%lu", g_scanner_cur_value(uzbl.scan).v_int);
1329 else if(token == G_TOKEN_IDENTIFIER) {
1330 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1332 else if(token == G_TOKEN_CHAR) {
1333 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1335 else if(token == G_TOKEN_ERROR) {
1336 g_scanner_error(uzbl.scan, "Token error in template ('%s') at line %d, column %d.",
1338 g_scanner_cur_line(uzbl.scan),
1339 g_scanner_cur_position(uzbl.scan));
1343 return g_string_free(ret, FALSE);
1345 /* --End Statusbar functions-- */
1348 sharg_append(GArray *a, const gchar *str) {
1349 const gchar *s = (str ? str : "");
1350 g_array_append_val(a, s);
1353 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1355 run_command (const gchar *command, const guint npre, const gchar **args,
1356 const gboolean sync, char **output_stdout) {
1357 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1360 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1361 gchar *pid = itos(getpid());
1362 gchar *xwin = itos(uzbl.xwin);
1364 sharg_append(a, command);
1365 for (i = 0; i < npre; i++) /* add n args before the default vars */
1366 sharg_append(a, args[i]);
1367 sharg_append(a, uzbl.state.config_file);
1368 sharg_append(a, pid);
1369 sharg_append(a, xwin);
1370 sharg_append(a, uzbl.comm.fifo_path);
1371 sharg_append(a, uzbl.comm.socket_path);
1372 sharg_append(a, uzbl.state.uri);
1373 sharg_append(a, uzbl.gui.main_title);
1375 for (i = npre; i < g_strv_length((gchar**)args); i++)
1376 sharg_append(a, args[i]);
1380 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1382 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1383 NULL, NULL, output_stdout, NULL, NULL, &err);
1384 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1385 NULL, NULL, NULL, &err);
1387 if (uzbl.state.verbose) {
1388 GString *s = g_string_new("spawned:");
1389 for (i = 0; i < (a->len); i++) {
1390 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1391 g_string_append_printf(s, " %s", qarg);
1394 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1395 printf("%s\n", s->str);
1396 g_string_free(s, TRUE);
1398 printf("Stdout: %s\n", *output_stdout);
1402 g_printerr("error on run_command: %s\n", err->message);
1407 g_array_free (a, TRUE);
1412 split_quoted(const gchar* src, const gboolean unquote) {
1413 /* split on unquoted space, return array of strings;
1414 remove a layer of quotes and backslashes if unquote */
1415 if (!src) return NULL;
1417 gboolean dq = FALSE;
1418 gboolean sq = FALSE;
1419 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1420 GString *s = g_string_new ("");
1424 for (p = src; *p != '\0'; p++) {
1425 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1426 else if (*p == '\\') { g_string_append_c(s, *p++);
1427 g_string_append_c(s, *p); }
1428 else if ((*p == '"') && unquote && !sq) dq = !dq;
1429 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1431 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1432 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1434 else if ((*p == ' ') && !dq && !sq) {
1435 dup = g_strdup(s->str);
1436 g_array_append_val(a, dup);
1437 g_string_truncate(s, 0);
1438 } else g_string_append_c(s, *p);
1440 dup = g_strdup(s->str);
1441 g_array_append_val(a, dup);
1442 ret = (gchar**)a->data;
1443 g_array_free (a, FALSE);
1444 g_string_free (s, TRUE);
1449 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1450 (void)web_view; (void)result;
1451 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1452 if (argv_idx(argv, 0))
1453 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1457 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1458 (void)web_view; (void)result;
1460 if (argv_idx(argv, 0))
1461 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1462 TRUE, &uzbl.comm.sync_stdout);
1466 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1467 (void)web_view; (void)result;
1468 if (!uzbl.behave.shell_cmd) {
1469 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1474 gchar *spacer = g_strdup("");
1475 g_array_insert_val(argv, 1, spacer);
1476 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1478 for (i = 1; i < g_strv_length(cmd); i++)
1479 g_array_prepend_val(argv, cmd[i]);
1481 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1487 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1488 (void)web_view; (void)result;
1489 if (!uzbl.behave.shell_cmd) {
1490 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1495 gchar *spacer = g_strdup("");
1496 g_array_insert_val(argv, 1, spacer);
1497 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1499 for (i = 1; i < g_strv_length(cmd); i++)
1500 g_array_prepend_val(argv, cmd[i]);
1502 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1503 TRUE, &uzbl.comm.sync_stdout);
1509 parse_command(const char *cmd, const char *param, GString *result) {
1512 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1514 gchar **par = split_quoted(param, TRUE);
1515 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1517 if (c->no_split) { /* don't split */
1518 sharg_append(a, param);
1520 for (i = 0; i < g_strv_length(par); i++)
1521 sharg_append(a, par[i]);
1524 if (result == NULL) {
1525 GString *result_print = g_string_new("");
1527 c->function(uzbl.gui.web_view, a, result_print);
1528 if (result_print->len)
1529 printf("%*s\n", result_print->len, result_print->str);
1531 g_string_free(result_print, TRUE);
1533 c->function(uzbl.gui.web_view, a, result);
1536 g_array_free (a, TRUE);
1539 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1546 if(*uzbl.net.proxy_url == ' '
1547 || uzbl.net.proxy_url == NULL) {
1548 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1549 (GType) SOUP_SESSION_PROXY_URI);
1552 suri = soup_uri_new(uzbl.net.proxy_url);
1553 g_object_set(G_OBJECT(uzbl.net.soup_session),
1554 SOUP_SESSION_PROXY_URI,
1556 soup_uri_free(suri);
1563 if(file_exists(uzbl.gui.icon)) {
1564 if (uzbl.gui.main_window)
1565 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1567 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1573 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1574 g_array_append_val (a, uzbl.state.uri);
1575 load_uri(uzbl.gui.web_view, a, NULL);
1576 g_array_free (a, TRUE);
1580 cmd_always_insert_mode() {
1581 uzbl.behave.insert_mode =
1582 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1588 g_object_set(G_OBJECT(uzbl.net.soup_session),
1589 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1593 cmd_max_conns_host() {
1594 g_object_set(G_OBJECT(uzbl.net.soup_session),
1595 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1600 soup_session_remove_feature
1601 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1602 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1603 /*g_free(uzbl.net.soup_logger);*/
1605 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1606 soup_session_add_feature(uzbl.net.soup_session,
1607 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1612 return webkit_web_view_get_settings(uzbl.gui.web_view);
1617 WebKitWebSettings *ws = view_settings();
1618 if (uzbl.behave.font_size > 0) {
1619 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1622 if (uzbl.behave.monospace_size > 0) {
1623 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1624 uzbl.behave.monospace_size, NULL);
1626 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1627 uzbl.behave.font_size, NULL);
1633 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1637 cmd_disable_plugins() {
1638 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1639 !uzbl.behave.disable_plugins, NULL);
1643 cmd_disable_scripts() {
1644 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1645 !uzbl.behave.disable_scripts, NULL);
1649 cmd_minimum_font_size() {
1650 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1651 uzbl.behave.minimum_font_size, NULL);
1654 cmd_autoload_img() {
1655 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1656 uzbl.behave.autoload_img, NULL);
1661 cmd_autoshrink_img() {
1662 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1663 uzbl.behave.autoshrink_img, NULL);
1668 cmd_enable_spellcheck() {
1669 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1670 uzbl.behave.enable_spellcheck, NULL);
1674 cmd_enable_private() {
1675 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1676 uzbl.behave.enable_private, NULL);
1681 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1682 uzbl.behave.print_bg, NULL);
1687 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1688 uzbl.behave.style_uri, NULL);
1692 cmd_resizable_txt() {
1693 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1694 uzbl.behave.resizable_txt, NULL);
1698 cmd_default_encoding() {
1699 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1700 uzbl.behave.default_encoding, NULL);
1704 cmd_enforce_96dpi() {
1705 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1706 uzbl.behave.enforce_96dpi, NULL);
1710 cmd_caret_browsing() {
1711 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1712 uzbl.behave.caret_browsing, NULL);
1716 cmd_cookie_handler() {
1717 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1718 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1719 if ((g_strcmp0(split[0], "sh") == 0) ||
1720 (g_strcmp0(split[0], "spawn") == 0)) {
1721 g_free (uzbl.behave.cookie_handler);
1722 uzbl.behave.cookie_handler =
1723 g_strdup_printf("sync_%s %s", split[0], split[1]);
1730 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1735 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1740 if(uzbl.behave.inject_html) {
1741 webkit_web_view_load_html_string (uzbl.gui.web_view,
1742 uzbl.behave.inject_html, NULL);
1751 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1752 uzbl.behave.modmask = 0;
1754 if(uzbl.behave.modkey)
1755 g_free(uzbl.behave.modkey);
1756 uzbl.behave.modkey = buf;
1758 for (i = 0; modkeys[i].key != NULL; i++) {
1759 if (g_strrstr(buf, modkeys[i].key))
1760 uzbl.behave.modmask |= modkeys[i].mask;
1766 if (*uzbl.net.useragent == ' ') {
1767 g_free (uzbl.net.useragent);
1768 uzbl.net.useragent = NULL;
1770 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1772 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1773 g_free(uzbl.net.useragent);
1774 uzbl.net.useragent = ua;
1780 gtk_widget_ref(uzbl.gui.scrolled_win);
1781 gtk_widget_ref(uzbl.gui.mainbar);
1782 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1783 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1785 if(uzbl.behave.status_top) {
1786 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1787 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1790 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.mainbar, FALSE, TRUE, 0);
1793 gtk_widget_unref(uzbl.gui.scrolled_win);
1794 gtk_widget_unref(uzbl.gui.mainbar);
1795 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1800 set_var_value(gchar *name, gchar *val) {
1801 uzbl_cmdprop *c = NULL;
1805 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1806 /* check for the variable type */
1807 if (c->type == TYPE_STR) {
1808 buf = expand(val, 0);
1811 } else if(c->type == TYPE_INT) {
1812 int *ip = (int *)c->ptr;
1813 buf = expand(val, 0);
1814 *ip = (int)strtoul(buf, &endp, 10);
1816 } else if (c->type == TYPE_FLOAT) {
1817 float *fp = (float *)c->ptr;
1818 buf = expand(val, 0);
1819 *fp = strtod(buf, &endp);
1823 /* invoke a command specific function */
1824 if(c->func) c->func();
1831 Behaviour *b = &uzbl.behave;
1833 if(b->html_buffer->str) {
1834 webkit_web_view_load_html_string (uzbl.gui.web_view,
1835 b->html_buffer->str, b->base_url);
1836 g_string_free(b->html_buffer, TRUE);
1837 b->html_buffer = g_string_new("");
1841 enum {M_CMD, M_HTML};
1843 parse_cmd_line(const char *ctl_line, GString *result) {
1844 Behaviour *b = &uzbl.behave;
1847 if(b->mode == M_HTML) {
1848 len = strlen(b->html_endmarker);
1849 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1850 if(len == strlen(ctl_line)-1 &&
1851 !strncmp(b->html_endmarker, ctl_line, len)) {
1853 set_var_value("mode", "0");
1858 set_timeout(b->html_timeout);
1859 g_string_append(b->html_buffer, ctl_line);
1862 else if((ctl_line[0] == '#') /* Comments */
1863 || (ctl_line[0] == ' ')
1864 || (ctl_line[0] == '\n'))
1865 ; /* ignore these lines */
1866 else { /* parse a command */
1868 gchar **tokens = NULL;
1869 len = strlen(ctl_line);
1871 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1872 ctlstrip = g_strndup(ctl_line, len - 1);
1873 else ctlstrip = g_strdup(ctl_line);
1875 tokens = g_strsplit(ctlstrip, " ", 2);
1876 parse_command(tokens[0], tokens[1], result);
1883 build_stream_name(int type, const gchar* dir) {
1884 char *xwin_str = NULL;
1885 State *s = &uzbl.state;
1888 xwin_str = itos((int)uzbl.xwin);
1890 str = g_strdup_printf
1891 ("%s/uzbl_fifo_%s", dir,
1892 s->instance_name ? s->instance_name : xwin_str);
1893 } else if (type == SOCKET) {
1894 str = g_strdup_printf
1895 ("%s/uzbl_socket_%s", dir,
1896 s->instance_name ? s->instance_name : xwin_str );
1903 control_fifo(GIOChannel *gio, GIOCondition condition) {
1904 if (uzbl.state.verbose)
1905 printf("triggered\n");
1910 if (condition & G_IO_HUP)
1911 g_error ("Fifo: Read end of pipe died!\n");
1914 g_error ("Fifo: GIOChannel broke\n");
1916 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1917 if (ret == G_IO_STATUS_ERROR) {
1918 g_error ("Fifo: Error reading: %s\n", err->message);
1922 parse_cmd_line(ctl_line, NULL);
1929 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1930 GIOChannel *chan = NULL;
1931 GError *error = NULL;
1932 gchar *path = build_stream_name(FIFO, dir);
1934 if (!file_exists(path)) {
1935 if (mkfifo (path, 0666) == 0) {
1936 // 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.
1937 chan = g_io_channel_new_file(path, "r+", &error);
1939 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1940 if (uzbl.state.verbose)
1941 printf ("init_fifo: created successfully as %s\n", path);
1942 uzbl.comm.fifo_path = path;
1944 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1945 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1946 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1947 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1949 /* if we got this far, there was an error; cleanup */
1950 if (error) g_error_free (error);
1957 control_stdin(GIOChannel *gio, GIOCondition condition) {
1959 gchar *ctl_line = NULL;
1962 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1963 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1966 parse_cmd_line(ctl_line, NULL);
1974 GIOChannel *chan = NULL;
1975 GError *error = NULL;
1977 chan = g_io_channel_unix_new(fileno(stdin));
1979 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1980 g_error ("Stdin: could not add watch\n");
1982 if (uzbl.state.verbose)
1983 printf ("Stdin: watch added successfully\n");
1986 g_error ("Stdin: Error while opening: %s\n", error->message);
1988 if (error) g_error_free (error);
1992 control_socket(GIOChannel *chan) {
1993 struct sockaddr_un remote;
1994 unsigned int t = sizeof(remote);
1996 GIOChannel *clientchan;
1998 clientsock = accept (g_io_channel_unix_get_fd(chan),
1999 (struct sockaddr *) &remote, &t);
2001 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2002 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2003 (GIOFunc) control_client_socket, clientchan);
2010 control_client_socket(GIOChannel *clientchan) {
2012 GString *result = g_string_new("");
2013 GError *error = NULL;
2017 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2018 if (ret == G_IO_STATUS_ERROR) {
2019 g_warning ("Error reading: %s\n", error->message);
2020 g_io_channel_shutdown(clientchan, TRUE, &error);
2022 } else if (ret == G_IO_STATUS_EOF) {
2023 /* shutdown and remove channel watch from main loop */
2024 g_io_channel_shutdown(clientchan, TRUE, &error);
2029 parse_cmd_line (ctl_line, result);
2030 g_string_append_c(result, '\n');
2031 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2033 if (ret == G_IO_STATUS_ERROR) {
2034 g_warning ("Error writing: %s", error->message);
2036 g_io_channel_flush(clientchan, &error);
2039 if (error) g_error_free (error);
2040 g_string_free(result, TRUE);
2046 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2047 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2048 if (unlink(uzbl.comm.socket_path) == -1)
2049 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2050 g_free(uzbl.comm.socket_path);
2051 uzbl.comm.socket_path = NULL;
2059 GIOChannel *chan = NULL;
2061 struct sockaddr_un local;
2062 gchar *path = build_stream_name(SOCKET, dir);
2064 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2066 local.sun_family = AF_UNIX;
2067 strcpy (local.sun_path, path);
2068 unlink (local.sun_path);
2070 len = strlen (local.sun_path) + sizeof (local.sun_family);
2071 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2072 if (uzbl.state.verbose)
2073 printf ("init_socket: opened in %s\n", path);
2076 if( (chan = g_io_channel_unix_new(sock)) ) {
2077 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2078 uzbl.comm.socket_path = path;
2081 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2083 /* if we got this far, there was an error; cleanup */
2090 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2091 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2093 // this function may be called very early when the templates are not set (yet), hence the checks
2095 update_title (void) {
2096 Behaviour *b = &uzbl.behave;
2099 if (b->show_status) {
2100 if (b->title_format_short) {
2101 parsed = expand_template(b->title_format_short, FALSE);
2102 if (uzbl.gui.main_window)
2103 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2106 if (b->status_format) {
2107 parsed = expand_template(b->status_format, TRUE);
2108 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2111 if (b->status_background) {
2113 gdk_color_parse (b->status_background, &color);
2114 //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)
2115 if (uzbl.gui.main_window)
2116 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2119 if (b->title_format_long) {
2120 parsed = expand_template(b->title_format_long, FALSE);
2121 if (uzbl.gui.main_window)
2122 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2129 key_press_cb (GtkWidget* window, GdkEventKey* event)
2131 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2135 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2136 || 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)
2139 /* turn off insert mode (if always_insert_mode is not used) */
2140 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2141 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2146 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2149 if (event->keyval == GDK_Escape) {
2150 g_string_truncate(uzbl.state.keycmd, 0);
2152 dehilight(uzbl.gui.web_view, NULL, NULL);
2156 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2157 if (event->keyval == GDK_Insert) {
2159 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2160 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2162 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2165 g_string_append (uzbl.state.keycmd, str);
2172 if (event->keyval == GDK_BackSpace)
2173 keycmd_bs(NULL, NULL, NULL);
2175 gboolean key_ret = FALSE;
2176 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2178 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2180 run_keycmd(key_ret);
2182 if (key_ret) return (!uzbl.behave.insert_mode);
2187 run_keycmd(const gboolean key_ret) {
2188 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2190 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2191 g_string_truncate(uzbl.state.keycmd, 0);
2192 parse_command(act->name, act->param, NULL);
2196 /* try if it's an incremental keycmd or one that takes args, and run it */
2197 GString* short_keys = g_string_new ("");
2198 GString* short_keys_inc = g_string_new ("");
2200 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2201 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2202 g_string_assign(short_keys_inc, short_keys->str);
2203 g_string_append_c(short_keys, '_');
2204 g_string_append_c(short_keys_inc, '*');
2206 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2207 /* run normal cmds only if return was pressed */
2208 exec_paramcmd(act, i);
2209 g_string_truncate(uzbl.state.keycmd, 0);
2211 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2212 if (key_ret) /* just quit the incremental command on return */
2213 g_string_truncate(uzbl.state.keycmd, 0);
2214 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2218 g_string_truncate(short_keys, short_keys->len - 1);
2220 g_string_free (short_keys, TRUE);
2221 g_string_free (short_keys_inc, TRUE);
2225 exec_paramcmd(const Action *act, const guint i) {
2226 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2227 GString *actionname = g_string_new ("");
2228 GString *actionparam = g_string_new ("");
2229 g_string_erase (parampart, 0, i+1);
2231 g_string_printf (actionname, act->name, parampart->str);
2233 g_string_printf (actionparam, act->param, parampart->str);
2234 parse_command(actionname->str, actionparam->str, NULL);
2235 g_string_free(actionname, TRUE);
2236 g_string_free(actionparam, TRUE);
2237 g_string_free(parampart, TRUE);
2245 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2246 //main_window_ref = g_object_ref(scrolled_window);
2247 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
2249 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2250 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2252 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2253 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2254 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2255 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2256 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2257 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2258 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2259 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2264 return scrolled_window;
2271 g->mainbar = gtk_hbox_new (FALSE, 0);
2273 /* keep a reference to the bar so we can re-pack it at runtime*/
2274 //sbar_ref = g_object_ref(g->mainbar);
2276 g->mainbar_label = gtk_label_new ("");
2277 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2278 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2279 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2280 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2281 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2282 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2288 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2289 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2290 gtk_widget_set_name (window, "Uzbl browser");
2291 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2292 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2298 GtkPlug* create_plug () {
2299 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2300 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2301 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2308 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2310 If actname is one that calls an external command, this function will inject
2311 newargs in front of the user-provided args in that command line. They will
2312 come become after the body of the script (in sh) or after the name of
2313 the command to execute (in spawn).
2314 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2315 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2317 The return value consist of two strings: the action (sh, ...) and its args.
2319 If act is not one that calls an external command, then the given action merely
2322 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2323 /* Arrr! Here be memory leaks */
2324 gchar *actdup = g_strdup(actname);
2325 g_array_append_val(rets, actdup);
2327 if ((g_strcmp0(actname, "spawn") == 0) ||
2328 (g_strcmp0(actname, "sh") == 0) ||
2329 (g_strcmp0(actname, "sync_spawn") == 0) ||
2330 (g_strcmp0(actname, "sync_sh") == 0)) {
2332 GString *a = g_string_new("");
2333 gchar **spawnparts = split_quoted(origargs, FALSE);
2334 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2335 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2337 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2338 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2340 g_array_append_val(rets, a->str);
2341 g_string_free(a, FALSE);
2342 g_strfreev(spawnparts);
2344 gchar *origdup = g_strdup(origargs);
2345 g_array_append_val(rets, origdup);
2347 return (gchar**)g_array_free(rets, FALSE);
2351 run_handler (const gchar *act, const gchar *args) {
2352 /* Consider this code a temporary hack to make the handlers usable.
2353 In practice, all this splicing, injection, and reconstruction is
2354 inefficient, annoying and hard to manage. Potential pitfalls arise
2355 when the handler specific args 1) are not quoted (the handler
2356 callbacks should take care of this) 2) are quoted but interfere
2357 with the users' own quotation. A more ideal solution is
2358 to refactor parse_command so that it doesn't just take a string
2359 and execute it; rather than that, we should have a function which
2360 returns the argument vector parsed from the string. This vector
2361 could be modified (e.g. insert additional args into it) before
2362 passing it to the next function that actually executes it. Though
2363 it still isn't perfect for chain actions.. will reconsider & re-
2364 factor when I have the time. -duc */
2366 char **parts = g_strsplit(act, " ", 2);
2368 if (g_strcmp0(parts[0], "chain") == 0) {
2369 GString *newargs = g_string_new("");
2370 gchar **chainparts = split_quoted(parts[1], FALSE);
2372 /* for every argument in the chain, inject the handler args
2373 and make sure the new parts are wrapped in quotes */
2374 gchar **cp = chainparts;
2376 gchar *quotless = NULL;
2377 gchar **spliced_quotless = NULL; // sigh -_-;
2378 gchar **inpart = NULL;
2381 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2383 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2384 } else quotless = g_strdup(*cp);
2386 spliced_quotless = g_strsplit(quotless, " ", 2);
2387 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2388 g_strfreev(spliced_quotless);
2390 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2396 parse_command(parts[0], &(newargs->str[1]), NULL);
2397 g_string_free(newargs, TRUE);
2398 g_strfreev(chainparts);
2401 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2402 parse_command(inparts[0], inparts[1], NULL);
2410 add_binding (const gchar *key, const gchar *act) {
2411 char **parts = g_strsplit(act, " ", 2);
2418 if (uzbl.state.verbose)
2419 printf ("Binding %-10s : %s\n", key, act);
2420 action = new_action(parts[0], parts[1]);
2422 if (g_hash_table_remove (uzbl.bindings, key))
2423 g_warning ("Overwriting existing binding for \"%s\"", key);
2424 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2429 get_xdg_var (XDG_Var xdg) {
2430 const gchar* actual_value = getenv (xdg.environmental);
2431 const gchar* home = getenv ("HOME");
2432 gchar* return_value;
2434 if (! actual_value || strcmp (actual_value, "") == 0) {
2435 if (xdg.default_value) {
2436 return_value = str_replace ("~", home, xdg.default_value);
2438 return_value = NULL;
2441 return_value = str_replace("~", home, actual_value);
2444 return return_value;
2448 find_xdg_file (int xdg_type, char* filename) {
2449 /* xdg_type = 0 => config
2450 xdg_type = 1 => data
2451 xdg_type = 2 => cache*/
2453 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2454 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2457 gchar* temporary_string;
2461 if (! file_exists (temporary_file) && xdg_type != 2) {
2462 buf = get_xdg_var (XDG[3 + xdg_type]);
2463 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2466 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2467 g_free (temporary_file);
2468 temporary_file = g_strconcat (temporary_string, filename, NULL);
2472 //g_free (temporary_string); - segfaults.
2474 if (file_exists (temporary_file)) {
2475 return temporary_file;
2482 State *s = &uzbl.state;
2483 Network *n = &uzbl.net;
2485 for (i = 0; default_config[i].command != NULL; i++) {
2486 parse_cmd_line(default_config[i].command, NULL);
2489 if (!s->config_file) {
2490 s->config_file = find_xdg_file (0, "/uzbl/config");
2493 if (s->config_file) {
2494 GArray* lines = read_file_by_line (s->config_file);
2498 while ((line = g_array_index(lines, gchar*, i))) {
2499 parse_cmd_line (line, NULL);
2503 g_array_free (lines, TRUE);
2505 if (uzbl.state.verbose)
2506 printf ("No configuration file loaded.\n");
2509 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2512 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2515 if (!uzbl.behave.cookie_handler)
2518 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2519 GString *s = g_string_new ("");
2520 SoupURI * soup_uri = soup_message_get_uri(msg);
2521 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2522 run_handler(uzbl.behave.cookie_handler, s->str);
2524 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2525 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2526 if ( p != NULL ) *p = '\0';
2527 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2529 if (uzbl.comm.sync_stdout)
2530 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2532 g_string_free(s, TRUE);
2536 save_cookies (SoupMessage *msg, gpointer user_data){
2540 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2541 cookie = soup_cookie_to_set_cookie_header(ck->data);
2542 SoupURI * soup_uri = soup_message_get_uri(msg);
2543 GString *s = g_string_new ("");
2544 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2545 run_handler(uzbl.behave.cookie_handler, s->str);
2547 g_string_free(s, TRUE);
2552 /* --- WEBINSPECTOR --- */
2554 hide_window_cb(GtkWidget *widget, gpointer data) {
2557 gtk_widget_hide(widget);
2561 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2564 (void) web_inspector;
2565 GtkWidget* scrolled_window;
2566 GtkWidget* new_web_view;
2569 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2570 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2571 G_CALLBACK(hide_window_cb), NULL);
2573 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2574 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2575 gtk_widget_show(g->inspector_window);
2577 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2578 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2579 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2580 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2581 gtk_widget_show(scrolled_window);
2583 new_web_view = webkit_web_view_new();
2584 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2586 return WEBKIT_WEB_VIEW(new_web_view);
2590 inspector_show_window_cb (WebKitWebInspector* inspector){
2592 gtk_widget_show(uzbl.gui.inspector_window);
2596 /* TODO: Add variables and code to make use of these functions */
2598 inspector_close_window_cb (WebKitWebInspector* inspector){
2604 inspector_attach_window_cb (WebKitWebInspector* inspector){
2610 inspector_detach_window_cb (WebKitWebInspector* inspector){
2616 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2622 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2628 set_up_inspector() {
2630 WebKitWebSettings *settings = view_settings();
2631 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2633 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2634 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2635 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2636 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2637 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2638 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2639 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2641 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2645 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2647 uzbl_cmdprop *c = v;
2652 if(c->type == TYPE_STR)
2653 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2654 else if(c->type == TYPE_INT)
2655 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2656 else if(c->type == TYPE_FLOAT)
2657 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2661 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2665 printf("bind %s = %s %s\n", (char *)k ,
2666 (char *)a->name, a->param?(char *)a->param:"");
2671 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2672 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2675 #ifndef UZBL_LIBRARY
2678 main (int argc, char* argv[]) {
2679 gtk_init (&argc, &argv);
2680 if (!g_thread_supported ())
2681 g_thread_init (NULL);
2682 uzbl.state.executable_path = g_strdup(argv[0]);
2683 uzbl.state.selected_url = NULL;
2684 uzbl.state.searchtx = NULL;
2686 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2687 g_option_context_add_main_entries (context, entries, NULL);
2688 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2689 g_option_context_parse (context, &argc, &argv, NULL);
2690 g_option_context_free(context);
2692 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2693 gboolean verbose_override = uzbl.state.verbose;
2695 /* initialize hash table */
2696 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2698 uzbl.net.soup_session = webkit_get_default_session();
2699 uzbl.state.keycmd = g_string_new("");
2701 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2702 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2703 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2704 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2705 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2706 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2709 if(uname(&uzbl.state.unameinfo) == -1)
2710 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2712 uzbl.gui.sbar.progress_s = g_strdup("=");
2713 uzbl.gui.sbar.progress_u = g_strdup("·");
2714 uzbl.gui.sbar.progress_w = 10;
2716 /* HTML mode defaults*/
2717 uzbl.behave.html_buffer = g_string_new("");
2718 uzbl.behave.html_endmarker = g_strdup(".");
2719 uzbl.behave.html_timeout = 60;
2720 uzbl.behave.base_url = g_strdup("http://invalid");
2722 /* default mode indicators */
2723 uzbl.behave.insert_indicator = g_strdup("I");
2724 uzbl.behave.cmd_indicator = g_strdup("C");
2728 make_var_to_name_hash();
2730 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2732 uzbl.gui.scrolled_win = create_browser();
2735 /* initial packing */
2736 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2737 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2739 if (uzbl.state.socket_id) {
2740 uzbl.gui.plug = create_plug ();
2741 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2742 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2744 uzbl.gui.main_window = create_window ();
2745 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2746 gtk_widget_show_all (uzbl.gui.main_window);
2747 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2750 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2752 if (uzbl.state.verbose) {
2753 printf("Uzbl start location: %s\n", argv[0]);
2754 if (uzbl.state.socket_id)
2755 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2757 printf("window_id %i\n",(int) uzbl.xwin);
2758 printf("pid %i\n", getpid ());
2759 printf("name: %s\n", uzbl.state.instance_name);
2762 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2763 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2764 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2765 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2766 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2770 if (!uzbl.behave.show_status)
2771 gtk_widget_hide(uzbl.gui.mainbar);
2780 if (verbose_override > uzbl.state.verbose)
2781 uzbl.state.verbose = verbose_override;
2784 set_var_value("uri", uri_override);
2785 g_free(uri_override);
2786 } else if (uzbl.state.uri)
2787 cmd_load_uri(uzbl.gui.web_view, NULL);
2792 return EXIT_SUCCESS;
2796 /* vi: set et ts=4: */