1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
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;
165 } const_name_to_ptr[] = {
166 { "WEBKIT_MAJOR", {(void*)WEBKIT_MAJOR_VERSION, TYPE_INT}},
167 { "WEBKIT_MINOR", {(void*)WEBKIT_MINOR_VERSION, TYPE_INT}},
168 { "WEBKIT_MICRO", {(void*)WEBKIT_MICRO_VERSION, TYPE_INT}},
169 { "SYSNAME", {&(uzbl.state.unameinfo.sysname), TYPE_STR}},
170 { "NODENAME", {&(uzbl.state.unameinfo.nodename), TYPE_STR}},
171 { "KERNREL", {&(uzbl.state.unameinfo.release), TYPE_STR}},
172 { "KERNVER", {&(uzbl.state.unameinfo.version), TYPE_STR}},
173 { "ARCH_SYSTEM", {&(uzbl.state.unameinfo.machine), TYPE_STR}},
174 { "ARCH_UZBL", {&(ARCH), TYPE_STR}},
176 { "DOMAINNAME", {&(uzbl.state.unameinfo.domainname), TYPE_STR}},
178 { "COMMIT", {&(COMMIT), TYPE_STR}},
180 { NULL, {NULL, TYPE_INT}}
181 }, *n2c_p = const_name_to_ptr;
187 { "SHIFT", GDK_SHIFT_MASK }, // shift
188 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
189 { "CONTROL", GDK_CONTROL_MASK }, // control
190 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
191 { "MOD2", GDK_MOD2_MASK }, // 5th mod
192 { "MOD3", GDK_MOD3_MASK }, // 6th mod
193 { "MOD4", GDK_MOD4_MASK }, // 7th mod
194 { "MOD5", GDK_MOD5_MASK }, // 8th mod
195 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
196 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
197 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
198 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
199 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
200 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
201 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
202 { "META", GDK_META_MASK }, // meta (since 2.10)
207 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
208 * for quick access */
210 make_var_to_name_hash() {
211 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
213 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
217 uzbl.comm.proto_const = g_hash_table_new(g_str_hash, g_str_equal);
219 g_hash_table_insert(uzbl.comm.proto_const, n2c_p->name, (gpointer) &n2c_p->cp);
224 /* --- UTILITY FUNCTIONS --- */
226 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
228 get_exp_type(gchar *s) {
232 else if(*(s+1) == '{')
233 return EXP_BRACED_VAR;
234 else if(*(s+1) == '<')
237 return EXP_SIMPLE_VAR;
243 * recurse == 1: don't expand '@(command)@'
244 * recurse == 2: don't expand '@<java script>@'
247 expand(char *s, guint recurse) {
251 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
256 gchar *cmd_stdout = NULL;
258 GString *buf = g_string_new("");
259 GString *js_ret = g_string_new("");
264 g_string_append_c(buf, *++s);
269 etype = get_exp_type(s);
274 if( (vend = strpbrk(s, end_simple_var)) ||
275 (vend = strchr(s, '\0')) ) {
276 strncpy(ret, s, vend-s);
282 if( (vend = strchr(s, upto)) ||
283 (vend = strchr(s, '\0')) ) {
284 strncpy(ret, s, vend-s);
290 strcpy(str_end, ")@");
292 if( (vend = strstr(s, str_end)) ||
293 (vend = strchr(s, '\0')) ) {
294 strncpy(ret, s, vend-s);
300 strcpy(str_end, ">@");
302 if( (vend = strstr(s, str_end)) ||
303 (vend = strchr(s, '\0')) ) {
304 strncpy(ret, s, vend-s);
310 if(etype == EXP_SIMPLE_VAR ||
311 etype == EXP_BRACED_VAR) {
314 if('A' <= ret[0] && 'Z' >= ret[0] &&
315 (c = g_hash_table_lookup(uzbl.comm.proto_const, ret))) {
317 } else if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
321 if(c && c->type == TYPE_STR) {
322 g_string_append(buf, (gchar *)ptr);
323 } else if(c && c->type == TYPE_INT) {
324 char *b = itos((uintptr_t)ptr);
325 g_string_append(buf, b);
329 if(etype == EXP_SIMPLE_VAR)
334 else if(recurse != 1 &&
336 mycmd = expand(ret, 1);
337 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
341 g_printerr("error on running command: %s\n", err->message);
344 else if (*cmd_stdout) {
345 g_string_append(buf, cmd_stdout);
350 else if(recurse != 2 &&
352 mycmd = expand(ret, 2);
353 eval_js(uzbl.gui.web_view, mycmd, js_ret);
357 g_string_append(buf, js_ret->str);
358 g_string_free(js_ret, TRUE);
359 js_ret = g_string_new("");
366 g_string_append_c(buf, *s);
371 g_string_free(js_ret, TRUE);
372 return g_string_free(buf, FALSE);
379 snprintf(tmp, sizeof(tmp), "%i", val);
380 return g_strdup(tmp);
384 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
387 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
390 str_replace (const char* search, const char* replace, const char* string) {
394 buf = g_strsplit (string, search, -1);
395 ret = g_strjoinv (replace, buf);
396 g_strfreev(buf); // somebody said this segfaults
402 read_file_by_line (gchar *path) {
403 GIOChannel *chan = NULL;
404 gchar *readbuf = NULL;
406 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
409 chan = g_io_channel_new_file(path, "r", NULL);
412 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
413 const gchar* val = g_strdup (readbuf);
414 g_array_append_val (lines, val);
419 g_io_channel_unref (chan);
421 fprintf(stderr, "File '%s' not be read.\n", path);
428 gchar* parseenv (char* string) {
429 extern char** environ;
430 gchar* tmpstr = NULL;
434 while (environ[i] != NULL) {
435 gchar** env = g_strsplit (environ[i], "=", 2);
436 gchar* envname = g_strconcat ("$", env[0], NULL);
438 if (g_strrstr (string, envname) != NULL) {
439 tmpstr = g_strdup(string);
441 string = str_replace(envname, env[1], tmpstr);
446 g_strfreev (env); // somebody said this breaks uzbl
454 setup_signal(int signr, sigfunc *shandler) {
455 struct sigaction nh, oh;
457 nh.sa_handler = shandler;
458 sigemptyset(&nh.sa_mask);
461 if(sigaction(signr, &nh, &oh) < 0)
469 if (uzbl.behave.fifo_dir)
470 unlink (uzbl.comm.fifo_path);
471 if (uzbl.behave.socket_dir)
472 unlink (uzbl.comm.socket_path);
474 g_free(uzbl.state.executable_path);
475 g_string_free(uzbl.state.keycmd, TRUE);
476 g_hash_table_destroy(uzbl.bindings);
477 g_hash_table_destroy(uzbl.behave.commands);
480 /* used for html_mode_timeout
481 * be sure to extend this function to use
482 * more timers if needed in other places
485 set_timeout(int seconds) {
487 memset(&t, 0, sizeof t);
489 t.it_value.tv_sec = seconds;
490 t.it_value.tv_usec = 0;
491 setitimer(ITIMER_REAL, &t, NULL);
494 /* --- SIGNAL HANDLER --- */
497 catch_sigterm(int s) {
503 catch_sigint(int s) {
513 set_var_value("mode", "0");
518 /* --- CALLBACKS --- */
521 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
524 (void) navigation_action;
525 (void) policy_decision;
527 const gchar* uri = webkit_network_request_get_uri (request);
528 if (uzbl.state.verbose)
529 printf("New window requested -> %s \n", uri);
530 new_window_load_uri(uri);
535 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
540 /* If we can display it, let's display it... */
541 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
542 webkit_web_policy_decision_use (policy_decision);
546 /* ...everything we can't displayed is downloaded */
547 webkit_web_policy_decision_download (policy_decision);
552 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
556 if (uzbl.state.selected_url != NULL) {
557 if (uzbl.state.verbose)
558 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
559 new_window_load_uri(uzbl.state.selected_url);
561 if (uzbl.state.verbose)
562 printf("New web view -> %s\n","Nothing to open, exiting");
568 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
571 if (uzbl.behave.download_handler) {
572 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
573 if (uzbl.state.verbose)
574 printf("Download -> %s\n",uri);
575 /* if urls not escaped, we may have to escape and quote uri before this call */
576 run_handler(uzbl.behave.download_handler, uri);
581 /* scroll a bar in a given direction */
583 scroll (GtkAdjustment* bar, GArray *argv) {
587 gdouble page_size = gtk_adjustment_get_page_size(bar);
588 gdouble value = gtk_adjustment_get_value(bar);
589 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
592 value += page_size * amount * 0.01;
596 max_value = gtk_adjustment_get_upper(bar) - page_size;
598 if (value > max_value)
599 value = max_value; /* don't scroll past the end of the page */
601 gtk_adjustment_set_value (bar, value);
605 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
606 (void) page; (void) argv; (void) result;
607 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
611 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
612 (void) page; (void) argv; (void) result;
613 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
614 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
618 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
619 (void) page; (void) result;
620 scroll(uzbl.gui.bar_v, argv);
624 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
625 (void) page; (void) result;
626 scroll(uzbl.gui.bar_h, argv);
631 if (!uzbl.behave.show_status) {
632 gtk_widget_hide(uzbl.gui.mainbar);
634 gtk_widget_show(uzbl.gui.mainbar);
640 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
645 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
649 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
654 if (uzbl.behave.show_status) {
655 gtk_widget_hide(uzbl.gui.mainbar);
657 gtk_widget_show(uzbl.gui.mainbar);
659 uzbl.behave.show_status = !uzbl.behave.show_status;
664 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
668 //Set selected_url state variable
669 g_free(uzbl.state.selected_url);
670 uzbl.state.selected_url = NULL;
672 uzbl.state.selected_url = g_strdup(link);
678 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
681 const gchar *title = webkit_web_view_get_title(web_view);
682 if (uzbl.gui.main_title)
683 g_free (uzbl.gui.main_title);
684 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
689 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
692 uzbl.gui.sbar.load_progress = progress;
697 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
701 if (uzbl.behave.load_finish_handler)
702 run_handler(uzbl.behave.load_finish_handler, "");
706 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
710 uzbl.gui.sbar.load_progress = 0;
711 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
712 if (uzbl.behave.load_start_handler)
713 run_handler(uzbl.behave.load_start_handler, "");
717 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
720 g_free (uzbl.state.uri);
721 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
722 uzbl.state.uri = g_string_free (newuri, FALSE);
723 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
724 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
727 if (uzbl.behave.load_commit_handler)
728 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
732 destroy_cb (GtkWidget* widget, gpointer data) {
740 if (uzbl.behave.history_handler) {
742 struct tm * timeinfo;
745 timeinfo = localtime ( &rawtime );
746 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
747 run_handler(uzbl.behave.history_handler, date);
752 /* VIEW funcs (little webkit wrappers) */
753 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
755 VIEWFUNC(reload_bypass_cache)
756 VIEWFUNC(stop_loading)
763 /* -- command to callback/function map for things we cannot attach to any signals */
764 static struct {char *key; CommandInfo value;} cmdlist[] =
765 { /* key function no_split */
766 { "back", {view_go_back, 0} },
767 { "forward", {view_go_forward, 0} },
768 { "scroll_vert", {scroll_vert, 0} },
769 { "scroll_horz", {scroll_horz, 0} },
770 { "scroll_begin", {scroll_begin, 0} },
771 { "scroll_end", {scroll_end, 0} },
772 { "reload", {view_reload, 0}, },
773 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
774 { "stop", {view_stop_loading, 0}, },
775 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
776 { "zoom_out", {view_zoom_out, 0}, },
777 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
778 { "uri", {load_uri, TRUE} },
779 { "js", {run_js, TRUE} },
780 { "script", {run_external_js, 0} },
781 { "toggle_status", {toggle_status_cb, 0} },
782 { "spawn", {spawn, 0} },
783 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
784 { "sh", {spawn_sh, 0} },
785 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
786 { "exit", {close_uzbl, 0} },
787 { "search", {search_forward_text, TRUE} },
788 { "search_reverse", {search_reverse_text, TRUE} },
789 { "dehilight", {dehilight, 0} },
790 { "toggle_insert_mode", {toggle_insert_mode, 0} },
791 { "set", {set_var, TRUE} },
792 //{ "get", {get_var, TRUE} },
793 { "bind", {act_bind, TRUE} },
794 { "dump_config", {act_dump_config, 0} },
795 { "keycmd", {keycmd, TRUE} },
796 { "keycmd_nl", {keycmd_nl, TRUE} },
797 { "keycmd_bs", {keycmd_bs, 0} },
798 { "chain", {chain, 0} },
799 { "print", {print, TRUE} }
806 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
808 for (i = 0; i < LENGTH(cmdlist); i++)
809 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
812 /* -- CORE FUNCTIONS -- */
815 free_action(gpointer act) {
816 Action *action = (Action*)act;
817 g_free(action->name);
819 g_free(action->param);
824 new_action(const gchar *name, const gchar *param) {
825 Action *action = g_new(Action, 1);
827 action->name = g_strdup(name);
829 action->param = g_strdup(param);
831 action->param = NULL;
837 file_exists (const char * filename) {
838 return (access(filename, F_OK) == 0);
842 set_var(WebKitWebView *page, GArray *argv, GString *result) {
843 (void) page; (void) result;
844 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
845 if (split[0] != NULL) {
846 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
847 set_var_value(g_strstrip(split[0]), value);
854 print(WebKitWebView *page, GArray *argv, GString *result) {
855 (void) page; (void) result;
858 buf = expand(argv_idx(argv, 0), 0);
859 g_string_assign(result, buf);
864 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
865 (void) page; (void) result;
866 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
867 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
868 add_binding(g_strstrip(split[0]), value);
880 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
881 (void) page; (void) result;
883 if (argv_idx(argv, 0)) {
884 if (strcmp (argv_idx(argv, 0), "0") == 0) {
885 uzbl.behave.insert_mode = FALSE;
887 uzbl.behave.insert_mode = TRUE;
890 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
897 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
900 if (argv_idx(argv, 0)) {
901 GString* newuri = g_string_new (argv_idx(argv, 0));
902 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
903 run_js(web_view, argv, NULL);
906 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
907 g_string_prepend (newuri, "http://");
908 /* if we do handle cookies, ask our handler for them */
909 webkit_web_view_load_uri (web_view, newuri->str);
910 g_string_free (newuri, TRUE);
918 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
919 size_t argumentCount, const JSValueRef arguments[],
920 JSValueRef* exception) {
925 JSStringRef js_result_string;
926 GString *result = g_string_new("");
928 if (argumentCount >= 1) {
929 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
930 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
931 char ctl_line[arg_size];
932 JSStringGetUTF8CString(arg, ctl_line, arg_size);
934 parse_cmd_line(ctl_line, result);
936 JSStringRelease(arg);
938 js_result_string = JSStringCreateWithUTF8CString(result->str);
940 g_string_free(result, TRUE);
942 return JSValueMakeString(ctx, js_result_string);
945 static JSStaticFunction js_static_functions[] = {
946 {"run", js_run_command, kJSPropertyAttributeNone},
951 /* This function creates the class and its definition, only once */
952 if (!uzbl.js.initialized) {
953 /* it would be pretty cool to make this dynamic */
954 uzbl.js.classdef = kJSClassDefinitionEmpty;
955 uzbl.js.classdef.staticFunctions = js_static_functions;
957 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
963 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
964 WebKitWebFrame *frame;
965 JSGlobalContextRef context;
966 JSObjectRef globalobject;
967 JSStringRef var_name;
969 JSStringRef js_script;
970 JSValueRef js_result;
971 JSStringRef js_result_string;
972 size_t js_result_size;
976 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
977 context = webkit_web_frame_get_global_context(frame);
978 globalobject = JSContextGetGlobalObject(context);
980 /* uzbl javascript namespace */
981 var_name = JSStringCreateWithUTF8CString("Uzbl");
982 JSObjectSetProperty(context, globalobject, var_name,
983 JSObjectMake(context, uzbl.js.classref, NULL),
984 kJSClassAttributeNone, NULL);
986 /* evaluate the script and get return value*/
987 js_script = JSStringCreateWithUTF8CString(script);
988 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
989 if (js_result && !JSValueIsUndefined(context, js_result)) {
990 js_result_string = JSValueToStringCopy(context, js_result, NULL);
991 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
993 if (js_result_size) {
994 char js_result_utf8[js_result_size];
995 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
996 g_string_assign(result, js_result_utf8);
999 JSStringRelease(js_result_string);
1003 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1005 JSStringRelease(var_name);
1006 JSStringRelease(js_script);
1010 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1012 if (argv_idx(argv, 0))
1013 eval_js(web_view, argv_idx(argv, 0), result);
1017 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1019 if (argv_idx(argv, 0)) {
1020 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1025 while ((line = g_array_index(lines, gchar*, i))) {
1027 js = g_strdup (line);
1029 gchar* newjs = g_strconcat (js, line, NULL);
1036 if (uzbl.state.verbose)
1037 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1039 if (argv_idx (argv, 1)) {
1040 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1044 eval_js (web_view, js, result);
1046 g_array_free (lines, TRUE);
1051 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1052 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1053 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1054 webkit_web_view_unmark_text_matches (page);
1055 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1056 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1060 if (uzbl.state.searchtx) {
1061 if (uzbl.state.verbose)
1062 printf ("Searching: %s\n", uzbl.state.searchtx);
1063 webkit_web_view_set_highlight_text_matches (page, TRUE);
1064 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1069 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1071 search_text(page, argv, TRUE);
1075 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1077 search_text(page, argv, FALSE);
1081 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1082 (void) argv; (void) result;
1083 webkit_web_view_set_highlight_text_matches (page, FALSE);
1088 new_window_load_uri (const gchar * uri) {
1089 GString* to_execute = g_string_new ("");
1090 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1092 for (i = 0; entries[i].long_name != NULL; i++) {
1093 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1094 gchar** str = (gchar**)entries[i].arg_data;
1096 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1100 if (uzbl.state.verbose)
1101 printf("\n%s\n", to_execute->str);
1102 g_spawn_command_line_async (to_execute->str, NULL);
1103 g_string_free (to_execute, TRUE);
1107 chain (WebKitWebView *page, GArray *argv, GString *result) {
1108 (void) page; (void) result;
1110 gchar **parts = NULL;
1112 while ((a = argv_idx(argv, i++))) {
1113 parts = g_strsplit (a, " ", 2);
1114 parse_command(parts[0], parts[1], result);
1120 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1124 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1130 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1134 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1140 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1145 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1147 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1152 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1159 /* --Statusbar functions-- */
1161 build_progressbar_ascii(int percent) {
1162 int width=uzbl.gui.sbar.progress_w;
1165 GString *bar = g_string_new("");
1167 l = (double)percent*((double)width/100.);
1168 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1170 for(i=0; i<(int)l; i++)
1171 g_string_append(bar, uzbl.gui.sbar.progress_s);
1174 g_string_append(bar, uzbl.gui.sbar.progress_u);
1176 return g_string_free(bar, FALSE);
1181 const GScannerConfig scan_config = {
1184 ) /* cset_skip_characters */,
1189 ) /* cset_identifier_first */,
1196 ) /* cset_identifier_nth */,
1197 ( "" ) /* cpair_comment_single */,
1199 TRUE /* case_sensitive */,
1201 FALSE /* skip_comment_multi */,
1202 FALSE /* skip_comment_single */,
1203 FALSE /* scan_comment_multi */,
1204 TRUE /* scan_identifier */,
1205 TRUE /* scan_identifier_1char */,
1206 FALSE /* scan_identifier_NULL */,
1207 TRUE /* scan_symbols */,
1208 FALSE /* scan_binary */,
1209 FALSE /* scan_octal */,
1210 FALSE /* scan_float */,
1211 FALSE /* scan_hex */,
1212 FALSE /* scan_hex_dollar */,
1213 FALSE /* scan_string_sq */,
1214 FALSE /* scan_string_dq */,
1215 TRUE /* numbers_2_int */,
1216 FALSE /* int_2_float */,
1217 FALSE /* identifier_2_string */,
1218 FALSE /* char_2_token */,
1219 FALSE /* symbol_2_token */,
1220 TRUE /* scope_0_fallback */,
1225 uzbl.scan = g_scanner_new(&scan_config);
1226 while(symp->symbol_name) {
1227 g_scanner_scope_add_symbol(uzbl.scan, 0,
1229 GINT_TO_POINTER(symp->symbol_token));
1235 expand_template(const char *template, gboolean escape_markup) {
1236 if(!template) return NULL;
1238 GTokenType token = G_TOKEN_NONE;
1239 GString *ret = g_string_new("");
1243 g_scanner_input_text(uzbl.scan, template, strlen(template));
1244 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1245 token = g_scanner_get_next_token(uzbl.scan);
1247 if(token == G_TOKEN_SYMBOL) {
1248 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1252 buf = uzbl.state.uri?
1253 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1254 g_string_append(ret, buf);
1258 g_string_append(ret, uzbl.state.uri?
1259 uzbl.state.uri:g_strdup(""));
1262 buf = itos(uzbl.gui.sbar.load_progress);
1263 g_string_append(ret, buf);
1266 case SYM_LOADPRGSBAR:
1267 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1268 g_string_append(ret, buf);
1273 buf = uzbl.gui.main_title?
1274 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1275 g_string_append(ret, buf);
1279 g_string_append(ret, uzbl.gui.main_title?
1280 uzbl.gui.main_title:g_strdup(""));
1282 case SYM_SELECTED_URI:
1284 buf = uzbl.state.selected_url?
1285 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1286 g_string_append(ret, buf);
1290 g_string_append(ret, uzbl.state.selected_url?
1291 uzbl.state.selected_url:g_strdup(""));
1294 buf = itos(uzbl.xwin);
1295 g_string_append(ret,
1296 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1301 buf = uzbl.state.keycmd->str?
1302 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1303 g_string_append(ret, buf);
1307 g_string_append(ret, uzbl.state.keycmd->str?
1308 uzbl.state.keycmd->str:g_strdup(""));
1311 g_string_append(ret,
1312 uzbl.behave.insert_mode?
1313 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1316 g_string_append(ret,
1317 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1319 /* useragent syms */
1324 else if(token == G_TOKEN_INT) {
1325 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1326 g_string_append(ret, buf);
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);
1337 return g_string_free(ret, FALSE);
1339 /* --End Statusbar functions-- */
1342 sharg_append(GArray *a, const gchar *str) {
1343 const gchar *s = (str ? str : "");
1344 g_array_append_val(a, s);
1347 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1349 run_command (const gchar *command, const guint npre, const gchar **args,
1350 const gboolean sync, char **output_stdout) {
1351 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1354 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1355 gchar *pid = itos(getpid());
1356 gchar *xwin = itos(uzbl.xwin);
1358 sharg_append(a, command);
1359 for (i = 0; i < npre; i++) /* add n args before the default vars */
1360 sharg_append(a, args[i]);
1361 sharg_append(a, uzbl.state.config_file);
1362 sharg_append(a, pid);
1363 sharg_append(a, xwin);
1364 sharg_append(a, uzbl.comm.fifo_path);
1365 sharg_append(a, uzbl.comm.socket_path);
1366 sharg_append(a, uzbl.state.uri);
1367 sharg_append(a, uzbl.gui.main_title);
1369 for (i = npre; i < g_strv_length((gchar**)args); i++)
1370 sharg_append(a, args[i]);
1374 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1376 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1377 NULL, NULL, output_stdout, NULL, NULL, &err);
1378 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1379 NULL, NULL, NULL, &err);
1381 if (uzbl.state.verbose) {
1382 GString *s = g_string_new("spawned:");
1383 for (i = 0; i < (a->len); i++) {
1384 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1385 g_string_append_printf(s, " %s", qarg);
1388 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1389 printf("%s\n", s->str);
1390 g_string_free(s, TRUE);
1392 printf("Stdout: %s\n", *output_stdout);
1396 g_printerr("error on run_command: %s\n", err->message);
1401 g_array_free (a, TRUE);
1406 split_quoted(const gchar* src, const gboolean unquote) {
1407 /* split on unquoted space, return array of strings;
1408 remove a layer of quotes and backslashes if unquote */
1409 if (!src) return NULL;
1411 gboolean dq = FALSE;
1412 gboolean sq = FALSE;
1413 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1414 GString *s = g_string_new ("");
1418 for (p = src; *p != '\0'; p++) {
1419 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1420 else if (*p == '\\') { g_string_append_c(s, *p++);
1421 g_string_append_c(s, *p); }
1422 else if ((*p == '"') && unquote && !sq) dq = !dq;
1423 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1425 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1426 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1428 else if ((*p == ' ') && !dq && !sq) {
1429 dup = g_strdup(s->str);
1430 g_array_append_val(a, dup);
1431 g_string_truncate(s, 0);
1432 } else g_string_append_c(s, *p);
1434 dup = g_strdup(s->str);
1435 g_array_append_val(a, dup);
1436 ret = (gchar**)a->data;
1437 g_array_free (a, FALSE);
1438 g_string_free (s, TRUE);
1443 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1444 (void)web_view; (void)result;
1445 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1446 if (argv_idx(argv, 0))
1447 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1451 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1452 (void)web_view; (void)result;
1454 if (argv_idx(argv, 0))
1455 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1456 TRUE, &uzbl.comm.sync_stdout);
1460 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1461 (void)web_view; (void)result;
1462 if (!uzbl.behave.shell_cmd) {
1463 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1468 gchar *spacer = g_strdup("");
1469 g_array_insert_val(argv, 1, spacer);
1470 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1472 for (i = 1; i < g_strv_length(cmd); i++)
1473 g_array_prepend_val(argv, cmd[i]);
1475 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1481 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1482 (void)web_view; (void)result;
1483 if (!uzbl.behave.shell_cmd) {
1484 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1489 gchar *spacer = g_strdup("");
1490 g_array_insert_val(argv, 1, spacer);
1491 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1493 for (i = 1; i < g_strv_length(cmd); i++)
1494 g_array_prepend_val(argv, cmd[i]);
1496 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1497 TRUE, &uzbl.comm.sync_stdout);
1503 parse_command(const char *cmd, const char *param, GString *result) {
1506 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1508 gchar **par = split_quoted(param, TRUE);
1509 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1511 if (c->no_split) { /* don't split */
1512 sharg_append(a, param);
1514 for (i = 0; i < g_strv_length(par); i++)
1515 sharg_append(a, par[i]);
1518 if (result == NULL) {
1519 GString *result_print = g_string_new("");
1521 c->function(uzbl.gui.web_view, a, result_print);
1522 if (result_print->len)
1523 printf("%*s\n", result_print->len, result_print->str);
1525 g_string_free(result_print, TRUE);
1527 c->function(uzbl.gui.web_view, a, result);
1530 g_array_free (a, TRUE);
1533 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1540 if(*uzbl.net.proxy_url == ' '
1541 || uzbl.net.proxy_url == NULL) {
1542 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1543 (GType) SOUP_SESSION_PROXY_URI);
1546 suri = soup_uri_new(uzbl.net.proxy_url);
1547 g_object_set(G_OBJECT(uzbl.net.soup_session),
1548 SOUP_SESSION_PROXY_URI,
1550 soup_uri_free(suri);
1557 if(file_exists(uzbl.gui.icon)) {
1558 if (uzbl.gui.main_window)
1559 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1561 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1563 g_free (uzbl.gui.icon);
1568 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1569 g_array_append_val (a, uzbl.state.uri);
1570 load_uri(uzbl.gui.web_view, a, NULL);
1571 g_array_free (a, TRUE);
1575 cmd_always_insert_mode() {
1576 uzbl.behave.insert_mode =
1577 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1583 g_object_set(G_OBJECT(uzbl.net.soup_session),
1584 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1588 cmd_max_conns_host() {
1589 g_object_set(G_OBJECT(uzbl.net.soup_session),
1590 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1595 soup_session_remove_feature
1596 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1597 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1598 /*g_free(uzbl.net.soup_logger);*/
1600 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1601 soup_session_add_feature(uzbl.net.soup_session,
1602 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1605 static WebKitWebSettings*
1607 return webkit_web_view_get_settings(uzbl.gui.web_view);
1612 WebKitWebSettings *ws = view_settings();
1613 if (uzbl.behave.font_size > 0) {
1614 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1617 if (uzbl.behave.monospace_size > 0) {
1618 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1619 uzbl.behave.monospace_size, NULL);
1621 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1622 uzbl.behave.font_size, NULL);
1628 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1632 cmd_disable_plugins() {
1633 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1634 !uzbl.behave.disable_plugins, NULL);
1638 cmd_disable_scripts() {
1639 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1640 !uzbl.behave.disable_scripts, NULL);
1644 cmd_minimum_font_size() {
1645 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1646 uzbl.behave.minimum_font_size, NULL);
1649 cmd_autoload_img() {
1650 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1651 uzbl.behave.autoload_img, NULL);
1656 cmd_autoshrink_img() {
1657 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1658 uzbl.behave.autoshrink_img, NULL);
1663 cmd_enable_spellcheck() {
1664 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1665 uzbl.behave.enable_spellcheck, NULL);
1669 cmd_enable_private() {
1670 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1671 uzbl.behave.enable_private, NULL);
1676 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1677 uzbl.behave.print_bg, NULL);
1682 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1683 uzbl.behave.style_uri, NULL);
1687 cmd_resizable_txt() {
1688 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1689 uzbl.behave.resizable_txt, NULL);
1693 cmd_default_encoding() {
1694 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1695 uzbl.behave.default_encoding, NULL);
1699 cmd_enforce_96dpi() {
1700 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1701 uzbl.behave.enforce_96dpi, NULL);
1705 cmd_caret_browsing() {
1706 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1707 uzbl.behave.caret_browsing, NULL);
1711 cmd_cookie_handler() {
1712 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1713 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1714 if ((g_strcmp0(split[0], "sh") == 0) ||
1715 (g_strcmp0(split[0], "spawn") == 0)) {
1716 g_free (uzbl.behave.cookie_handler);
1717 uzbl.behave.cookie_handler =
1718 g_strdup_printf("sync_%s %s", split[0], split[1]);
1725 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1730 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1735 if(uzbl.behave.inject_html) {
1736 webkit_web_view_load_html_string (uzbl.gui.web_view,
1737 uzbl.behave.inject_html, NULL);
1746 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1747 uzbl.behave.modmask = 0;
1749 if(uzbl.behave.modkey)
1750 g_free(uzbl.behave.modkey);
1751 uzbl.behave.modkey = buf;
1753 for (i = 0; modkeys[i].key != NULL; i++) {
1754 if (g_strrstr(buf, modkeys[i].key))
1755 uzbl.behave.modmask |= modkeys[i].mask;
1761 if (*uzbl.net.useragent == ' ') {
1762 g_free (uzbl.net.useragent);
1763 uzbl.net.useragent = NULL;
1765 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1766 uzbl.net.useragent, NULL);
1772 gtk_widget_ref(uzbl.gui.scrolled_win);
1773 gtk_widget_ref(uzbl.gui.mainbar);
1774 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1775 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1777 if(uzbl.behave.status_top) {
1778 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1779 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1782 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1783 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1785 gtk_widget_unref(uzbl.gui.scrolled_win);
1786 gtk_widget_unref(uzbl.gui.mainbar);
1787 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1792 set_var_value(gchar *name, gchar *val) {
1793 uzbl_cmdprop *c = NULL;
1797 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1798 /* check for the variable type */
1799 if (c->type == TYPE_STR) {
1800 buf = expand(val, 0);
1803 } else if(c->type == TYPE_INT) {
1804 int *ip = (int *)c->ptr;
1805 buf = expand(val, 0);
1806 *ip = (int)strtoul(buf, &endp, 10);
1808 } else if (c->type == TYPE_FLOAT) {
1809 float *fp = (float *)c->ptr;
1810 buf = expand(val, 0);
1811 *fp = strtod(buf, &endp);
1815 /* invoke a command specific function */
1816 if(c->func) c->func();
1823 Behaviour *b = &uzbl.behave;
1825 if(b->html_buffer->str) {
1826 webkit_web_view_load_html_string (uzbl.gui.web_view,
1827 b->html_buffer->str, b->base_url);
1828 g_string_free(b->html_buffer, TRUE);
1829 b->html_buffer = g_string_new("");
1833 enum {M_CMD, M_HTML};
1835 parse_cmd_line(const char *ctl_line, GString *result) {
1836 Behaviour *b = &uzbl.behave;
1839 if(b->mode == M_HTML) {
1840 len = strlen(b->html_endmarker);
1841 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1842 if(len == strlen(ctl_line)-1 &&
1843 !strncmp(b->html_endmarker, ctl_line, len)) {
1845 set_var_value("mode", "0");
1850 set_timeout(b->html_timeout);
1851 g_string_append(b->html_buffer, ctl_line);
1854 else if((ctl_line[0] == '#') /* Comments */
1855 || (ctl_line[0] == ' ')
1856 || (ctl_line[0] == '\n'))
1857 ; /* ignore these lines */
1858 else { /* parse a command */
1860 gchar **tokens = NULL;
1861 len = strlen(ctl_line);
1863 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1864 ctlstrip = g_strndup(ctl_line, len - 1);
1865 else ctlstrip = g_strdup(ctl_line);
1867 tokens = g_strsplit(ctlstrip, " ", 2);
1868 parse_command(tokens[0], tokens[1], result);
1875 build_stream_name(int type, const gchar* dir) {
1876 char *xwin_str = NULL;
1877 State *s = &uzbl.state;
1880 xwin_str = itos((int)uzbl.xwin);
1882 str = g_strdup_printf
1883 ("%s/uzbl_fifo_%s", dir,
1884 s->instance_name ? s->instance_name : xwin_str);
1885 } else if (type == SOCKET) {
1886 str = g_strdup_printf
1887 ("%s/uzbl_socket_%s", dir,
1888 s->instance_name ? s->instance_name : xwin_str );
1895 control_fifo(GIOChannel *gio, GIOCondition condition) {
1896 if (uzbl.state.verbose)
1897 printf("triggered\n");
1902 if (condition & G_IO_HUP)
1903 g_error ("Fifo: Read end of pipe died!\n");
1906 g_error ("Fifo: GIOChannel broke\n");
1908 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1909 if (ret == G_IO_STATUS_ERROR) {
1910 g_error ("Fifo: Error reading: %s\n", err->message);
1914 parse_cmd_line(ctl_line, NULL);
1921 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1922 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1923 if (unlink(uzbl.comm.fifo_path) == -1)
1924 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1925 g_free(uzbl.comm.fifo_path);
1926 uzbl.comm.fifo_path = NULL;
1929 if (*dir == ' ') { /* space unsets the variable */
1934 GIOChannel *chan = NULL;
1935 GError *error = NULL;
1936 gchar *path = build_stream_name(FIFO, dir);
1938 if (!file_exists(path)) {
1939 if (mkfifo (path, 0666) == 0) {
1940 // 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.
1941 chan = g_io_channel_new_file(path, "r+", &error);
1943 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1944 if (uzbl.state.verbose)
1945 printf ("init_fifo: created successfully as %s\n", path);
1946 uzbl.comm.fifo_path = path;
1948 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1949 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1950 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1951 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1953 /* if we got this far, there was an error; cleanup */
1954 if (error) g_error_free (error);
1961 control_stdin(GIOChannel *gio, GIOCondition condition) {
1963 gchar *ctl_line = NULL;
1966 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1967 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1970 parse_cmd_line(ctl_line, NULL);
1978 GIOChannel *chan = NULL;
1979 GError *error = NULL;
1981 chan = g_io_channel_unix_new(fileno(stdin));
1983 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1984 g_error ("Stdin: could not add watch\n");
1986 if (uzbl.state.verbose)
1987 printf ("Stdin: watch added successfully\n");
1990 g_error ("Stdin: Error while opening: %s\n", error->message);
1992 if (error) g_error_free (error);
1996 control_socket(GIOChannel *chan) {
1997 struct sockaddr_un remote;
1998 unsigned int t = sizeof(remote);
2000 GIOChannel *clientchan;
2002 clientsock = accept (g_io_channel_unix_get_fd(chan),
2003 (struct sockaddr *) &remote, &t);
2005 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2006 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2007 (GIOFunc) control_client_socket, clientchan);
2014 control_client_socket(GIOChannel *clientchan) {
2016 GString *result = g_string_new("");
2017 GError *error = NULL;
2021 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2022 if (ret == G_IO_STATUS_ERROR) {
2023 g_warning ("Error reading: %s\n", error->message);
2024 g_io_channel_shutdown(clientchan, TRUE, &error);
2026 } else if (ret == G_IO_STATUS_EOF) {
2027 /* shutdown and remove channel watch from main loop */
2028 g_io_channel_shutdown(clientchan, TRUE, &error);
2033 parse_cmd_line (ctl_line, result);
2034 g_string_append_c(result, '\n');
2035 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2037 if (ret == G_IO_STATUS_ERROR) {
2038 g_warning ("Error writing: %s", error->message);
2040 g_io_channel_flush(clientchan, &error);
2043 if (error) g_error_free (error);
2044 g_string_free(result, TRUE);
2050 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2051 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2052 if (unlink(uzbl.comm.socket_path) == -1)
2053 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2054 g_free(uzbl.comm.socket_path);
2055 uzbl.comm.socket_path = NULL;
2063 GIOChannel *chan = NULL;
2065 struct sockaddr_un local;
2066 gchar *path = build_stream_name(SOCKET, dir);
2068 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2070 local.sun_family = AF_UNIX;
2071 strcpy (local.sun_path, path);
2072 unlink (local.sun_path);
2074 len = strlen (local.sun_path) + sizeof (local.sun_family);
2075 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2076 if (uzbl.state.verbose)
2077 printf ("init_socket: opened in %s\n", path);
2080 if( (chan = g_io_channel_unix_new(sock)) ) {
2081 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2082 uzbl.comm.socket_path = path;
2085 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2087 /* if we got this far, there was an error; cleanup */
2094 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2095 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2097 // this function may be called very early when the templates are not set (yet), hence the checks
2099 update_title (void) {
2100 Behaviour *b = &uzbl.behave;
2103 if (b->show_status) {
2104 if (b->title_format_short) {
2105 parsed = expand_template(b->title_format_short, FALSE);
2106 if (uzbl.gui.main_window)
2107 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2110 if (b->status_format) {
2111 parsed = expand_template(b->status_format, TRUE);
2112 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2115 if (b->status_background) {
2117 gdk_color_parse (b->status_background, &color);
2118 //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2119 if (uzbl.gui.main_window)
2120 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2121 else if (uzbl.gui.plug)
2122 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2125 if (b->title_format_long) {
2126 parsed = expand_template(b->title_format_long, FALSE);
2127 if (uzbl.gui.main_window)
2128 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2135 key_press_cb (GtkWidget* window, GdkEventKey* event)
2137 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2141 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2142 || 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)
2145 /* turn off insert mode (if always_insert_mode is not used) */
2146 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2147 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2152 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2155 if (event->keyval == GDK_Escape) {
2156 g_string_truncate(uzbl.state.keycmd, 0);
2158 dehilight(uzbl.gui.web_view, NULL, NULL);
2162 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2163 if (event->keyval == GDK_Insert) {
2165 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2166 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2168 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2171 g_string_append (uzbl.state.keycmd, str);
2178 if (event->keyval == GDK_BackSpace)
2179 keycmd_bs(NULL, NULL, NULL);
2181 gboolean key_ret = FALSE;
2182 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2184 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2186 run_keycmd(key_ret);
2188 if (key_ret) return (!uzbl.behave.insert_mode);
2193 run_keycmd(const gboolean key_ret) {
2194 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2196 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2197 g_string_truncate(uzbl.state.keycmd, 0);
2198 parse_command(act->name, act->param, NULL);
2202 /* try if it's an incremental keycmd or one that takes args, and run it */
2203 GString* short_keys = g_string_new ("");
2204 GString* short_keys_inc = g_string_new ("");
2206 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2207 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2208 g_string_assign(short_keys_inc, short_keys->str);
2209 g_string_append_c(short_keys, '_');
2210 g_string_append_c(short_keys_inc, '*');
2212 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2213 /* run normal cmds only if return was pressed */
2214 exec_paramcmd(act, i);
2215 g_string_truncate(uzbl.state.keycmd, 0);
2217 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2218 if (key_ret) /* just quit the incremental command on return */
2219 g_string_truncate(uzbl.state.keycmd, 0);
2220 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2224 g_string_truncate(short_keys, short_keys->len - 1);
2226 g_string_free (short_keys, TRUE);
2227 g_string_free (short_keys_inc, TRUE);
2231 exec_paramcmd(const Action *act, const guint i) {
2232 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2233 GString *actionname = g_string_new ("");
2234 GString *actionparam = g_string_new ("");
2235 g_string_erase (parampart, 0, i+1);
2237 g_string_printf (actionname, act->name, parampart->str);
2239 g_string_printf (actionparam, act->param, parampart->str);
2240 parse_command(actionname->str, actionparam->str, NULL);
2241 g_string_free(actionname, TRUE);
2242 g_string_free(actionparam, TRUE);
2243 g_string_free(parampart, TRUE);
2251 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2252 //main_window_ref = g_object_ref(scrolled_window);
2253 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
2255 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2256 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2258 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2259 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2268 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2270 return scrolled_window;
2277 g->mainbar = gtk_hbox_new (FALSE, 0);
2279 /* keep a reference to the bar so we can re-pack it at runtime*/
2280 //sbar_ref = g_object_ref(g->mainbar);
2282 g->mainbar_label = gtk_label_new ("");
2283 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2284 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2285 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2286 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2287 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2288 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2293 GtkWidget* create_window () {
2294 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2295 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2296 gtk_widget_set_name (window, "Uzbl browser");
2297 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2298 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2304 GtkPlug* create_plug () {
2305 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2306 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2307 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2314 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2316 If actname is one that calls an external command, this function will inject
2317 newargs in front of the user-provided args in that command line. They will
2318 come become after the body of the script (in sh) or after the name of
2319 the command to execute (in spawn).
2320 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2321 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2323 The return value consist of two strings: the action (sh, ...) and its args.
2325 If act is not one that calls an external command, then the given action merely
2328 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2329 gchar *actdup = g_strdup(actname);
2330 g_array_append_val(rets, actdup);
2332 if ((g_strcmp0(actname, "spawn") == 0) ||
2333 (g_strcmp0(actname, "sh") == 0) ||
2334 (g_strcmp0(actname, "sync_spawn") == 0) ||
2335 (g_strcmp0(actname, "sync_sh") == 0)) {
2337 GString *a = g_string_new("");
2338 gchar **spawnparts = split_quoted(origargs, FALSE);
2339 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2340 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2342 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2343 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2345 g_array_append_val(rets, a->str);
2346 g_string_free(a, FALSE);
2347 g_strfreev(spawnparts);
2349 gchar *origdup = g_strdup(origargs);
2350 g_array_append_val(rets, origdup);
2352 return (gchar**)g_array_free(rets, FALSE);
2356 run_handler (const gchar *act, const gchar *args) {
2357 /* Consider this code a temporary hack to make the handlers usable.
2358 In practice, all this splicing, injection, and reconstruction is
2359 inefficient, annoying and hard to manage. Potential pitfalls arise
2360 when the handler specific args 1) are not quoted (the handler
2361 callbacks should take care of this) 2) are quoted but interfere
2362 with the users' own quotation. A more ideal solution is
2363 to refactor parse_command so that it doesn't just take a string
2364 and execute it; rather than that, we should have a function which
2365 returns the argument vector parsed from the string. This vector
2366 could be modified (e.g. insert additional args into it) before
2367 passing it to the next function that actually executes it. Though
2368 it still isn't perfect for chain actions.. will reconsider & re-
2369 factor when I have the time. -duc */
2371 char **parts = g_strsplit(act, " ", 2);
2373 if (g_strcmp0(parts[0], "chain") == 0) {
2374 GString *newargs = g_string_new("");
2375 gchar **chainparts = split_quoted(parts[1], FALSE);
2377 /* for every argument in the chain, inject the handler args
2378 and make sure the new parts are wrapped in quotes */
2379 gchar **cp = chainparts;
2381 gchar *quotless = NULL;
2382 gchar **spliced_quotless = NULL; // sigh -_-;
2383 gchar **inpart = NULL;
2386 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2388 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2389 } else quotless = g_strdup(*cp);
2391 spliced_quotless = g_strsplit(quotless, " ", 2);
2392 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2393 g_strfreev(spliced_quotless);
2395 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2401 parse_command(parts[0], &(newargs->str[1]), NULL);
2402 g_string_free(newargs, TRUE);
2403 g_strfreev(chainparts);
2406 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2407 parse_command(inparts[0], inparts[1], NULL);
2415 add_binding (const gchar *key, const gchar *act) {
2416 char **parts = g_strsplit(act, " ", 2);
2423 if (uzbl.state.verbose)
2424 printf ("Binding %-10s : %s\n", key, act);
2425 action = new_action(parts[0], parts[1]);
2427 if (g_hash_table_remove (uzbl.bindings, key))
2428 g_warning ("Overwriting existing binding for \"%s\"", key);
2429 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2434 get_xdg_var (XDG_Var xdg) {
2435 const gchar* actual_value = getenv (xdg.environmental);
2436 const gchar* home = getenv ("HOME");
2437 gchar* return_value;
2439 if (! actual_value || strcmp (actual_value, "") == 0) {
2440 if (xdg.default_value) {
2441 return_value = str_replace ("~", home, xdg.default_value);
2443 return_value = NULL;
2446 return_value = str_replace("~", home, actual_value);
2449 return return_value;
2453 find_xdg_file (int xdg_type, char* filename) {
2454 /* xdg_type = 0 => config
2455 xdg_type = 1 => data
2456 xdg_type = 2 => cache*/
2458 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2459 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2462 gchar* temporary_string;
2466 if (! file_exists (temporary_file) && xdg_type != 2) {
2467 buf = get_xdg_var (XDG[3 + xdg_type]);
2468 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2471 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2472 g_free (temporary_file);
2473 temporary_file = g_strconcat (temporary_string, filename, NULL);
2477 //g_free (temporary_string); - segfaults.
2479 if (file_exists (temporary_file)) {
2480 return temporary_file;
2487 State *s = &uzbl.state;
2488 Network *n = &uzbl.net;
2490 for (i = 0; default_config[i].command != NULL; i++) {
2491 parse_cmd_line(default_config[i].command, NULL);
2494 if (g_strcmp0(s->config_file, "-") == 0) {
2495 s->config_file = NULL;
2499 else if (!s->config_file) {
2500 s->config_file = find_xdg_file (0, "/uzbl/config");
2503 if (s->config_file) {
2504 GArray* lines = read_file_by_line (s->config_file);
2508 while ((line = g_array_index(lines, gchar*, i))) {
2509 parse_cmd_line (line, NULL);
2513 g_array_free (lines, TRUE);
2515 if (uzbl.state.verbose)
2516 printf ("No configuration file loaded.\n");
2519 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2522 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2525 if (!uzbl.behave.cookie_handler)
2528 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2529 GString *s = g_string_new ("");
2530 SoupURI * soup_uri = soup_message_get_uri(msg);
2531 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2532 run_handler(uzbl.behave.cookie_handler, s->str);
2534 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2535 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2536 if ( p != NULL ) *p = '\0';
2537 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2539 if (uzbl.comm.sync_stdout)
2540 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2542 g_string_free(s, TRUE);
2546 save_cookies (SoupMessage *msg, gpointer user_data){
2550 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2551 cookie = soup_cookie_to_set_cookie_header(ck->data);
2552 SoupURI * soup_uri = soup_message_get_uri(msg);
2553 GString *s = g_string_new ("");
2554 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2555 run_handler(uzbl.behave.cookie_handler, s->str);
2557 g_string_free(s, TRUE);
2562 /* --- WEBINSPECTOR --- */
2564 hide_window_cb(GtkWidget *widget, gpointer data) {
2567 gtk_widget_hide(widget);
2570 static WebKitWebView*
2571 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2574 (void) web_inspector;
2575 GtkWidget* scrolled_window;
2576 GtkWidget* new_web_view;
2579 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2580 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2581 G_CALLBACK(hide_window_cb), NULL);
2583 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2584 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2585 gtk_widget_show(g->inspector_window);
2587 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2588 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2589 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2590 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2591 gtk_widget_show(scrolled_window);
2593 new_web_view = webkit_web_view_new();
2594 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2596 return WEBKIT_WEB_VIEW(new_web_view);
2600 inspector_show_window_cb (WebKitWebInspector* inspector){
2602 gtk_widget_show(uzbl.gui.inspector_window);
2606 /* TODO: Add variables and code to make use of these functions */
2608 inspector_close_window_cb (WebKitWebInspector* inspector){
2614 inspector_attach_window_cb (WebKitWebInspector* inspector){
2620 inspector_detach_window_cb (WebKitWebInspector* inspector){
2626 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2632 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2638 set_up_inspector() {
2640 WebKitWebSettings *settings = view_settings();
2641 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2643 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2644 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2645 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2646 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2647 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2648 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2649 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2651 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2655 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2657 uzbl_cmdprop *c = v;
2662 if(c->type == TYPE_STR)
2663 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2664 else if(c->type == TYPE_INT)
2665 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2669 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2673 printf("bind %s = %s %s\n", (char *)k ,
2674 (char *)a->name, a->param?(char *)a->param:"");
2679 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2680 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2683 #ifndef UZBL_LIBRARY
2686 main (int argc, char* argv[]) {
2687 gtk_init (&argc, &argv);
2688 if (!g_thread_supported ())
2689 g_thread_init (NULL);
2690 uzbl.state.executable_path = g_strdup(argv[0]);
2691 uzbl.state.selected_url = NULL;
2692 uzbl.state.searchtx = NULL;
2694 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2695 g_option_context_add_main_entries (context, entries, NULL);
2696 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2697 g_option_context_parse (context, &argc, &argv, NULL);
2698 g_option_context_free(context);
2700 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2701 gboolean verbose_override = uzbl.state.verbose;
2703 /* initialize hash table */
2704 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2706 uzbl.net.soup_session = webkit_get_default_session();
2707 uzbl.state.keycmd = g_string_new("");
2709 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2710 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2711 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2712 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2713 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2714 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2717 if(uname(&uzbl.state.unameinfo) == -1)
2718 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2720 uzbl.gui.sbar.progress_s = g_strdup("=");
2721 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2722 uzbl.gui.sbar.progress_w = 10;
2724 /* HTML mode defaults*/
2725 uzbl.behave.html_buffer = g_string_new("");
2726 uzbl.behave.html_endmarker = g_strdup(".");
2727 uzbl.behave.html_timeout = 60;
2728 uzbl.behave.base_url = g_strdup("http://invalid");
2730 /* default mode indicators */
2731 uzbl.behave.insert_indicator = g_strdup("I");
2732 uzbl.behave.cmd_indicator = g_strdup("C");
2736 make_var_to_name_hash();
2738 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2740 uzbl.gui.scrolled_win = create_browser();
2743 /* initial packing */
2744 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2745 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2747 if (uzbl.state.socket_id) {
2748 uzbl.gui.plug = create_plug ();
2749 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2750 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2752 uzbl.gui.main_window = create_window ();
2753 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2754 gtk_widget_show_all (uzbl.gui.main_window);
2755 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2758 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2760 if (uzbl.state.verbose) {
2761 printf("Uzbl start location: %s\n", argv[0]);
2762 if (uzbl.state.socket_id)
2763 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2765 printf("window_id %i\n",(int) uzbl.xwin);
2766 printf("pid %i\n", getpid ());
2767 printf("name: %s\n", uzbl.state.instance_name);
2770 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2771 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2772 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2773 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2774 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2778 if (!uzbl.behave.show_status)
2779 gtk_widget_hide(uzbl.gui.mainbar);
2786 if (verbose_override > uzbl.state.verbose)
2787 uzbl.state.verbose = verbose_override;
2790 set_var_value("uri", uri_override);
2791 g_free(uri_override);
2792 } else if (uzbl.state.uri)
2793 cmd_load_uri(uzbl.gui.web_view, NULL);
2798 return EXIT_SUCCESS;
2802 /* vi: set et ts=4: */