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 {
90 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
92 /* abbreviations to help keep the table's width humane */
93 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
94 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* ---------------------------------------------------------------------------------------------- */
102 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
104 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
105 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
106 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
107 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
108 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
109 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
110 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
111 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
112 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
113 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
114 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
115 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
116 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
117 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_title)},
118 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
119 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
120 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
121 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
122 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, NULL)},
123 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
124 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
125 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
126 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
127 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
128 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
129 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
130 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
131 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
132 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
133 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
134 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
135 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
136 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
137 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
138 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
139 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
140 /* exported WebKitWebSettings properties */
141 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
142 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
143 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
144 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
145 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
146 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
147 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
148 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
149 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
150 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
151 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
152 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
153 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
154 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
155 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
156 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
158 /* constants (not dumpable or writeable) */
159 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
160 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
161 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
162 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
163 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
165 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
166 }, *n2v_p = var_name_to_ptr;
173 { "SHIFT", GDK_SHIFT_MASK }, // shift
174 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
175 { "CONTROL", GDK_CONTROL_MASK }, // control
176 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
177 { "MOD2", GDK_MOD2_MASK }, // 5th mod
178 { "MOD3", GDK_MOD3_MASK }, // 6th mod
179 { "MOD4", GDK_MOD4_MASK }, // 7th mod
180 { "MOD5", GDK_MOD5_MASK }, // 8th mod
181 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
182 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
183 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
184 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
185 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
186 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
187 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
188 { "META", GDK_META_MASK }, // meta (since 2.10)
193 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
194 * for quick access */
196 make_var_to_name_hash() {
197 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
199 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
204 /* --- UTILITY FUNCTIONS --- */
206 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
208 get_exp_type(gchar *s) {
212 else if(*(s+1) == '{')
213 return EXP_BRACED_VAR;
214 else if(*(s+1) == '<')
217 return EXP_SIMPLE_VAR;
223 * recurse == 1: don't expand '@(command)@'
224 * recurse == 2: don't expand '@<java script>@'
227 expand(char *s, guint recurse, gboolean escape_markup) {
231 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
236 gchar *cmd_stdout = NULL;
238 GString *buf = g_string_new("");
239 GString *js_ret = g_string_new("");
244 g_string_append_c(buf, *++s);
249 etype = get_exp_type(s);
254 if( (vend = strpbrk(s, end_simple_var)) ||
255 (vend = strchr(s, '\0')) ) {
256 strncpy(ret, s, vend-s);
262 if( (vend = strchr(s, upto)) ||
263 (vend = strchr(s, '\0')) ) {
264 strncpy(ret, s, vend-s);
270 strcpy(str_end, ")@");
272 if( (vend = strstr(s, str_end)) ||
273 (vend = strchr(s, '\0')) ) {
274 strncpy(ret, s, vend-s);
280 strcpy(str_end, ">@");
282 if( (vend = strstr(s, str_end)) ||
283 (vend = strchr(s, '\0')) ) {
284 strncpy(ret, s, vend-s);
290 if(etype == EXP_SIMPLE_VAR ||
291 etype == EXP_BRACED_VAR) {
292 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
293 if(c->type == TYPE_STR) {
295 char *b = g_markup_escape_text((gchar *)*c->ptr,
296 strlen((gchar *)*c->ptr));
297 g_string_append(buf, b);
300 g_string_append(buf, (gchar *)*c->ptr);
302 } else if(c && c->type == TYPE_INT) {
303 char *b = itos((uintptr_t)*c->ptr);
304 g_string_append(buf, b);
309 if(etype == EXP_SIMPLE_VAR)
314 else if(recurse != 1 &&
316 mycmd = expand(ret, 1, escape_markup);
317 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
321 g_printerr("error on running command: %s\n", err->message);
324 else if (*cmd_stdout) {
325 int len = strlen(cmd_stdout);
327 if(cmd_stdout[len-1] == '\n')
328 cmd_stdout[--len] = 0; /* strip trailing newline */
331 char *b = g_markup_escape_text(cmd_stdout, len);
332 g_string_append(buf, b);
335 g_string_append(buf, cmd_stdout);
341 else if(recurse != 2 &&
343 mycmd = expand(ret, 2, escape_markup);
344 eval_js(uzbl.gui.web_view, mycmd, js_ret);
349 char *b = g_markup_escape_text(js_ret->str,
350 strlen(js_ret->str));
351 g_string_append(buf, b);
354 g_string_append(buf, js_ret->str);
356 g_string_free(js_ret, TRUE);
357 js_ret = g_string_new("");
364 g_string_append_c(buf, *s);
369 g_string_free(js_ret, TRUE);
370 return g_string_free(buf, FALSE);
377 snprintf(tmp, sizeof(tmp), "%i", val);
378 return g_strdup(tmp);
382 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
385 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
388 str_replace (const char* search, const char* replace, const char* string) {
392 buf = g_strsplit (string, search, -1);
393 ret = g_strjoinv (replace, buf);
394 g_strfreev(buf); // somebody said this segfaults
400 read_file_by_line (gchar *path) {
401 GIOChannel *chan = NULL;
402 gchar *readbuf = NULL;
404 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
407 chan = g_io_channel_new_file(path, "r", NULL);
410 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
411 const gchar* val = g_strdup (readbuf);
412 g_array_append_val (lines, val);
417 g_io_channel_unref (chan);
419 fprintf(stderr, "File '%s' not be read.\n", path);
426 gchar* parseenv (char* string) {
427 extern char** environ;
428 gchar* tmpstr = NULL;
432 while (environ[i] != NULL) {
433 gchar** env = g_strsplit (environ[i], "=", 2);
434 gchar* envname = g_strconcat ("$", env[0], NULL);
436 if (g_strrstr (string, envname) != NULL) {
437 tmpstr = g_strdup(string);
439 string = str_replace(envname, env[1], tmpstr);
444 g_strfreev (env); // somebody said this breaks uzbl
452 setup_signal(int signr, sigfunc *shandler) {
453 struct sigaction nh, oh;
455 nh.sa_handler = shandler;
456 sigemptyset(&nh.sa_mask);
459 if(sigaction(signr, &nh, &oh) < 0)
467 if (uzbl.behave.fifo_dir)
468 unlink (uzbl.comm.fifo_path);
469 if (uzbl.behave.socket_dir)
470 unlink (uzbl.comm.socket_path);
472 g_free(uzbl.state.executable_path);
473 g_string_free(uzbl.state.keycmd, TRUE);
474 g_hash_table_destroy(uzbl.bindings);
475 g_hash_table_destroy(uzbl.behave.commands);
478 /* used for html_mode_timeout
479 * be sure to extend this function to use
480 * more timers if needed in other places
483 set_timeout(int seconds) {
485 memset(&t, 0, sizeof t);
487 t.it_value.tv_sec = seconds;
488 t.it_value.tv_usec = 0;
489 setitimer(ITIMER_REAL, &t, NULL);
492 /* --- SIGNAL HANDLER --- */
495 catch_sigterm(int s) {
501 catch_sigint(int s) {
511 set_var_value("mode", "0");
516 /* --- CALLBACKS --- */
519 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
522 (void) navigation_action;
523 (void) policy_decision;
525 const gchar* uri = webkit_network_request_get_uri (request);
526 if (uzbl.state.verbose)
527 printf("New window requested -> %s \n", uri);
528 new_window_load_uri(uri);
533 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
538 /* If we can display it, let's display it... */
539 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
540 webkit_web_policy_decision_use (policy_decision);
544 /* ...everything we can't displayed is downloaded */
545 webkit_web_policy_decision_download (policy_decision);
550 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
554 if (uzbl.state.selected_url != NULL) {
555 if (uzbl.state.verbose)
556 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
557 new_window_load_uri(uzbl.state.selected_url);
559 if (uzbl.state.verbose)
560 printf("New web view -> %s\n","Nothing to open, exiting");
566 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
569 if (uzbl.behave.download_handler) {
570 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
571 if (uzbl.state.verbose)
572 printf("Download -> %s\n",uri);
573 /* if urls not escaped, we may have to escape and quote uri before this call */
574 run_handler(uzbl.behave.download_handler, uri);
579 /* scroll a bar in a given direction */
581 scroll (GtkAdjustment* bar, GArray *argv) {
585 gdouble page_size = gtk_adjustment_get_page_size(bar);
586 gdouble value = gtk_adjustment_get_value(bar);
587 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
590 value += page_size * amount * 0.01;
594 max_value = gtk_adjustment_get_upper(bar) - page_size;
596 if (value > max_value)
597 value = max_value; /* don't scroll past the end of the page */
599 gtk_adjustment_set_value (bar, value);
603 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
604 (void) page; (void) argv; (void) result;
605 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
609 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
610 (void) page; (void) argv; (void) result;
611 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
612 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
616 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) result;
618 scroll(uzbl.gui.bar_v, argv);
622 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
623 (void) page; (void) result;
624 scroll(uzbl.gui.bar_h, argv);
629 if (!uzbl.behave.show_status) {
630 gtk_widget_hide(uzbl.gui.mainbar);
632 gtk_widget_show(uzbl.gui.mainbar);
638 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
643 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
647 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
652 if (uzbl.behave.show_status) {
653 gtk_widget_hide(uzbl.gui.mainbar);
655 gtk_widget_show(uzbl.gui.mainbar);
657 uzbl.behave.show_status = !uzbl.behave.show_status;
662 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
666 //Set selected_url state variable
667 g_free(uzbl.state.selected_url);
668 uzbl.state.selected_url = NULL;
670 uzbl.state.selected_url = g_strdup(link);
676 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
679 const gchar *title = webkit_web_view_get_title(web_view);
680 if (uzbl.gui.main_title)
681 g_free (uzbl.gui.main_title);
682 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
687 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
690 uzbl.gui.sbar.load_progress = progress;
695 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
699 if (uzbl.behave.load_finish_handler)
700 run_handler(uzbl.behave.load_finish_handler, "");
704 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
708 uzbl.gui.sbar.load_progress = 0;
709 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
710 if (uzbl.behave.load_start_handler)
711 run_handler(uzbl.behave.load_start_handler, "");
715 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
718 g_free (uzbl.state.uri);
719 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
720 uzbl.state.uri = g_string_free (newuri, FALSE);
721 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
722 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
725 if (uzbl.behave.load_commit_handler)
726 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
730 destroy_cb (GtkWidget* widget, gpointer data) {
738 if (uzbl.behave.history_handler) {
740 struct tm * timeinfo;
743 timeinfo = localtime ( &rawtime );
744 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
745 run_handler(uzbl.behave.history_handler, date);
750 /* VIEW funcs (little webkit wrappers) */
751 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
753 VIEWFUNC(reload_bypass_cache)
754 VIEWFUNC(stop_loading)
761 /* -- command to callback/function map for things we cannot attach to any signals */
762 static struct {char *key; CommandInfo value;} cmdlist[] =
763 { /* key function no_split */
764 { "back", {view_go_back, 0} },
765 { "forward", {view_go_forward, 0} },
766 { "scroll_vert", {scroll_vert, 0} },
767 { "scroll_horz", {scroll_horz, 0} },
768 { "scroll_begin", {scroll_begin, 0} },
769 { "scroll_end", {scroll_end, 0} },
770 { "reload", {view_reload, 0}, },
771 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
772 { "stop", {view_stop_loading, 0}, },
773 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
774 { "zoom_out", {view_zoom_out, 0}, },
775 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
776 { "uri", {load_uri, TRUE} },
777 { "js", {run_js, TRUE} },
778 { "script", {run_external_js, 0} },
779 { "toggle_status", {toggle_status_cb, 0} },
780 { "spawn", {spawn, 0} },
781 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
782 { "sh", {spawn_sh, 0} },
783 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
784 { "exit", {close_uzbl, 0} },
785 { "search", {search_forward_text, TRUE} },
786 { "search_reverse", {search_reverse_text, TRUE} },
787 { "dehilight", {dehilight, 0} },
788 { "toggle_insert_mode", {toggle_insert_mode, 0} },
789 { "set", {set_var, TRUE} },
790 //{ "get", {get_var, TRUE} },
791 { "bind", {act_bind, TRUE} },
792 { "dump_config", {act_dump_config, 0} },
793 { "keycmd", {keycmd, TRUE} },
794 { "keycmd_nl", {keycmd_nl, TRUE} },
795 { "keycmd_bs", {keycmd_bs, 0} },
796 { "chain", {chain, 0} },
797 { "print", {print, TRUE} }
804 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
806 for (i = 0; i < LENGTH(cmdlist); i++)
807 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
810 /* -- CORE FUNCTIONS -- */
813 free_action(gpointer act) {
814 Action *action = (Action*)act;
815 g_free(action->name);
817 g_free(action->param);
822 new_action(const gchar *name, const gchar *param) {
823 Action *action = g_new(Action, 1);
825 action->name = g_strdup(name);
827 action->param = g_strdup(param);
829 action->param = NULL;
835 file_exists (const char * filename) {
836 return (access(filename, F_OK) == 0);
840 set_var(WebKitWebView *page, GArray *argv, GString *result) {
841 (void) page; (void) result;
842 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
843 if (split[0] != NULL) {
844 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
845 set_var_value(g_strstrip(split[0]), value);
852 print(WebKitWebView *page, GArray *argv, GString *result) {
853 (void) page; (void) result;
856 buf = expand(argv_idx(argv, 0), 0, FALSE);
857 g_string_assign(result, buf);
862 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
863 (void) page; (void) result;
864 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
865 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
866 add_binding(g_strstrip(split[0]), value);
878 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
879 (void) page; (void) result;
881 if (argv_idx(argv, 0)) {
882 if (strcmp (argv_idx(argv, 0), "0") == 0) {
883 uzbl.behave.insert_mode = FALSE;
885 uzbl.behave.insert_mode = TRUE;
888 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
895 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
898 if (argv_idx(argv, 0)) {
899 GString* newuri = g_string_new (argv_idx(argv, 0));
900 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
901 run_js(web_view, argv, NULL);
904 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
905 g_string_prepend (newuri, "http://");
906 /* if we do handle cookies, ask our handler for them */
907 webkit_web_view_load_uri (web_view, newuri->str);
908 g_string_free (newuri, TRUE);
916 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
917 size_t argumentCount, const JSValueRef arguments[],
918 JSValueRef* exception) {
923 JSStringRef js_result_string;
924 GString *result = g_string_new("");
926 if (argumentCount >= 1) {
927 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
928 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
929 char ctl_line[arg_size];
930 JSStringGetUTF8CString(arg, ctl_line, arg_size);
932 parse_cmd_line(ctl_line, result);
934 JSStringRelease(arg);
936 js_result_string = JSStringCreateWithUTF8CString(result->str);
938 g_string_free(result, TRUE);
940 return JSValueMakeString(ctx, js_result_string);
943 static JSStaticFunction js_static_functions[] = {
944 {"run", js_run_command, kJSPropertyAttributeNone},
949 /* This function creates the class and its definition, only once */
950 if (!uzbl.js.initialized) {
951 /* it would be pretty cool to make this dynamic */
952 uzbl.js.classdef = kJSClassDefinitionEmpty;
953 uzbl.js.classdef.staticFunctions = js_static_functions;
955 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
961 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
962 WebKitWebFrame *frame;
963 JSGlobalContextRef context;
964 JSObjectRef globalobject;
965 JSStringRef var_name;
967 JSStringRef js_script;
968 JSValueRef js_result;
969 JSStringRef js_result_string;
970 size_t js_result_size;
974 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
975 context = webkit_web_frame_get_global_context(frame);
976 globalobject = JSContextGetGlobalObject(context);
978 /* uzbl javascript namespace */
979 var_name = JSStringCreateWithUTF8CString("Uzbl");
980 JSObjectSetProperty(context, globalobject, var_name,
981 JSObjectMake(context, uzbl.js.classref, NULL),
982 kJSClassAttributeNone, NULL);
984 /* evaluate the script and get return value*/
985 js_script = JSStringCreateWithUTF8CString(script);
986 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
987 if (js_result && !JSValueIsUndefined(context, js_result)) {
988 js_result_string = JSValueToStringCopy(context, js_result, NULL);
989 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
991 if (js_result_size) {
992 char js_result_utf8[js_result_size];
993 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
994 g_string_assign(result, js_result_utf8);
997 JSStringRelease(js_result_string);
1001 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1003 JSStringRelease(var_name);
1004 JSStringRelease(js_script);
1008 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1010 if (argv_idx(argv, 0))
1011 eval_js(web_view, argv_idx(argv, 0), result);
1015 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1017 if (argv_idx(argv, 0)) {
1018 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1023 while ((line = g_array_index(lines, gchar*, i))) {
1025 js = g_strdup (line);
1027 gchar* newjs = g_strconcat (js, line, NULL);
1034 if (uzbl.state.verbose)
1035 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1037 if (argv_idx (argv, 1)) {
1038 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1042 eval_js (web_view, js, result);
1044 g_array_free (lines, TRUE);
1049 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1050 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1051 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1052 webkit_web_view_unmark_text_matches (page);
1053 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1054 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1058 if (uzbl.state.searchtx) {
1059 if (uzbl.state.verbose)
1060 printf ("Searching: %s\n", uzbl.state.searchtx);
1061 webkit_web_view_set_highlight_text_matches (page, TRUE);
1062 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1067 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1069 search_text(page, argv, TRUE);
1073 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1075 search_text(page, argv, FALSE);
1079 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1080 (void) argv; (void) result;
1081 webkit_web_view_set_highlight_text_matches (page, FALSE);
1086 new_window_load_uri (const gchar * uri) {
1087 GString* to_execute = g_string_new ("");
1088 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1090 for (i = 0; entries[i].long_name != NULL; i++) {
1091 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1092 gchar** str = (gchar**)entries[i].arg_data;
1094 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1098 if (uzbl.state.verbose)
1099 printf("\n%s\n", to_execute->str);
1100 g_spawn_command_line_async (to_execute->str, NULL);
1101 g_string_free (to_execute, TRUE);
1105 chain (WebKitWebView *page, GArray *argv, GString *result) {
1106 (void) page; (void) result;
1108 gchar **parts = NULL;
1110 while ((a = argv_idx(argv, i++))) {
1111 parts = g_strsplit (a, " ", 2);
1112 parse_command(parts[0], parts[1], result);
1118 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1122 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1128 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1132 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1138 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1143 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1145 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1150 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1157 /* --Statusbar functions-- */
1159 build_progressbar_ascii(int percent) {
1160 int width=uzbl.gui.sbar.progress_w;
1163 GString *bar = g_string_new("");
1165 l = (double)percent*((double)width/100.);
1166 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1168 for(i=0; i<(int)l; i++)
1169 g_string_append(bar, uzbl.gui.sbar.progress_s);
1172 g_string_append(bar, uzbl.gui.sbar.progress_u);
1174 return g_string_free(bar, FALSE);
1179 const GScannerConfig scan_config = {
1182 ) /* cset_skip_characters */,
1187 ) /* cset_identifier_first */,
1194 ) /* cset_identifier_nth */,
1195 ( "" ) /* cpair_comment_single */,
1197 TRUE /* case_sensitive */,
1199 FALSE /* skip_comment_multi */,
1200 FALSE /* skip_comment_single */,
1201 FALSE /* scan_comment_multi */,
1202 TRUE /* scan_identifier */,
1203 TRUE /* scan_identifier_1char */,
1204 FALSE /* scan_identifier_NULL */,
1205 TRUE /* scan_symbols */,
1206 FALSE /* scan_binary */,
1207 FALSE /* scan_octal */,
1208 FALSE /* scan_float */,
1209 FALSE /* scan_hex */,
1210 FALSE /* scan_hex_dollar */,
1211 FALSE /* scan_string_sq */,
1212 FALSE /* scan_string_dq */,
1213 TRUE /* numbers_2_int */,
1214 FALSE /* int_2_float */,
1215 FALSE /* identifier_2_string */,
1216 FALSE /* char_2_token */,
1217 FALSE /* symbol_2_token */,
1218 TRUE /* scope_0_fallback */,
1223 uzbl.scan = g_scanner_new(&scan_config);
1224 while(symp->symbol_name) {
1225 g_scanner_scope_add_symbol(uzbl.scan, 0,
1227 GINT_TO_POINTER(symp->symbol_token));
1233 expand_template(const char *template, gboolean escape_markup) {
1234 if(!template) return NULL;
1236 GTokenType token = G_TOKEN_NONE;
1237 GString *ret = g_string_new("");
1241 g_scanner_input_text(uzbl.scan, template, strlen(template));
1242 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1243 token = g_scanner_get_next_token(uzbl.scan);
1245 if(token == G_TOKEN_SYMBOL) {
1246 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1250 buf = uzbl.state.uri?
1251 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1252 g_string_append(ret, buf);
1256 g_string_append(ret, uzbl.state.uri?
1257 uzbl.state.uri:g_strdup(""));
1260 buf = itos(uzbl.gui.sbar.load_progress);
1261 g_string_append(ret, buf);
1264 case SYM_LOADPRGSBAR:
1265 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1266 g_string_append(ret, buf);
1271 buf = uzbl.gui.main_title?
1272 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1273 g_string_append(ret, buf);
1277 g_string_append(ret, uzbl.gui.main_title?
1278 uzbl.gui.main_title:g_strdup(""));
1280 case SYM_SELECTED_URI:
1282 buf = uzbl.state.selected_url?
1283 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1284 g_string_append(ret, buf);
1288 g_string_append(ret, uzbl.state.selected_url?
1289 uzbl.state.selected_url:g_strdup(""));
1292 buf = itos(uzbl.xwin);
1293 g_string_append(ret,
1294 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1299 buf = uzbl.state.keycmd->str?
1300 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1301 g_string_append(ret, buf);
1305 g_string_append(ret, uzbl.state.keycmd->str?
1306 uzbl.state.keycmd->str:g_strdup(""));
1309 g_string_append(ret,
1310 uzbl.behave.insert_mode?
1311 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1314 g_string_append(ret,
1315 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1317 /* useragent syms */
1322 else if(token == G_TOKEN_INT) {
1323 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1324 g_string_append(ret, buf);
1327 else if(token == G_TOKEN_IDENTIFIER) {
1328 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1330 else if(token == G_TOKEN_CHAR) {
1331 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1335 return g_string_free(ret, FALSE);
1337 /* --End Statusbar functions-- */
1340 sharg_append(GArray *a, const gchar *str) {
1341 const gchar *s = (str ? str : "");
1342 g_array_append_val(a, s);
1345 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1347 run_command (const gchar *command, const guint npre, const gchar **args,
1348 const gboolean sync, char **output_stdout) {
1349 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1352 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1353 gchar *pid = itos(getpid());
1354 gchar *xwin = itos(uzbl.xwin);
1356 sharg_append(a, command);
1357 for (i = 0; i < npre; i++) /* add n args before the default vars */
1358 sharg_append(a, args[i]);
1359 sharg_append(a, uzbl.state.config_file);
1360 sharg_append(a, pid);
1361 sharg_append(a, xwin);
1362 sharg_append(a, uzbl.comm.fifo_path);
1363 sharg_append(a, uzbl.comm.socket_path);
1364 sharg_append(a, uzbl.state.uri);
1365 sharg_append(a, uzbl.gui.main_title);
1367 for (i = npre; i < g_strv_length((gchar**)args); i++)
1368 sharg_append(a, args[i]);
1372 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1374 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1375 NULL, NULL, output_stdout, NULL, NULL, &err);
1376 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1377 NULL, NULL, NULL, &err);
1379 if (uzbl.state.verbose) {
1380 GString *s = g_string_new("spawned:");
1381 for (i = 0; i < (a->len); i++) {
1382 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1383 g_string_append_printf(s, " %s", qarg);
1386 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1387 printf("%s\n", s->str);
1388 g_string_free(s, TRUE);
1390 printf("Stdout: %s\n", *output_stdout);
1394 g_printerr("error on run_command: %s\n", err->message);
1399 g_array_free (a, TRUE);
1404 split_quoted(const gchar* src, const gboolean unquote) {
1405 /* split on unquoted space, return array of strings;
1406 remove a layer of quotes and backslashes if unquote */
1407 if (!src) return NULL;
1409 gboolean dq = FALSE;
1410 gboolean sq = FALSE;
1411 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1412 GString *s = g_string_new ("");
1416 for (p = src; *p != '\0'; p++) {
1417 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1418 else if (*p == '\\') { g_string_append_c(s, *p++);
1419 g_string_append_c(s, *p); }
1420 else if ((*p == '"') && unquote && !sq) dq = !dq;
1421 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1423 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1424 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1426 else if ((*p == ' ') && !dq && !sq) {
1427 dup = g_strdup(s->str);
1428 g_array_append_val(a, dup);
1429 g_string_truncate(s, 0);
1430 } else g_string_append_c(s, *p);
1432 dup = g_strdup(s->str);
1433 g_array_append_val(a, dup);
1434 ret = (gchar**)a->data;
1435 g_array_free (a, FALSE);
1436 g_string_free (s, TRUE);
1441 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1442 (void)web_view; (void)result;
1443 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1444 if (argv_idx(argv, 0))
1445 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1449 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1450 (void)web_view; (void)result;
1452 if (argv_idx(argv, 0))
1453 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1454 TRUE, &uzbl.comm.sync_stdout);
1458 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1459 (void)web_view; (void)result;
1460 if (!uzbl.behave.shell_cmd) {
1461 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1466 gchar *spacer = g_strdup("");
1467 g_array_insert_val(argv, 1, spacer);
1468 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1470 for (i = 1; i < g_strv_length(cmd); i++)
1471 g_array_prepend_val(argv, cmd[i]);
1473 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1479 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1480 (void)web_view; (void)result;
1481 if (!uzbl.behave.shell_cmd) {
1482 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1487 gchar *spacer = g_strdup("");
1488 g_array_insert_val(argv, 1, spacer);
1489 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1491 for (i = 1; i < g_strv_length(cmd); i++)
1492 g_array_prepend_val(argv, cmd[i]);
1494 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1495 TRUE, &uzbl.comm.sync_stdout);
1501 parse_command(const char *cmd, const char *param, GString *result) {
1504 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1506 gchar **par = split_quoted(param, TRUE);
1507 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1509 if (c->no_split) { /* don't split */
1510 sharg_append(a, param);
1512 for (i = 0; i < g_strv_length(par); i++)
1513 sharg_append(a, par[i]);
1516 if (result == NULL) {
1517 GString *result_print = g_string_new("");
1519 c->function(uzbl.gui.web_view, a, result_print);
1520 if (result_print->len)
1521 printf("%*s\n", result_print->len, result_print->str);
1523 g_string_free(result_print, TRUE);
1525 c->function(uzbl.gui.web_view, a, result);
1528 g_array_free (a, TRUE);
1531 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1538 if(*uzbl.net.proxy_url == ' '
1539 || uzbl.net.proxy_url == NULL) {
1540 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1541 (GType) SOUP_SESSION_PROXY_URI);
1544 suri = soup_uri_new(uzbl.net.proxy_url);
1545 g_object_set(G_OBJECT(uzbl.net.soup_session),
1546 SOUP_SESSION_PROXY_URI,
1548 soup_uri_free(suri);
1555 if(file_exists(uzbl.gui.icon)) {
1556 if (uzbl.gui.main_window)
1557 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1559 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1561 g_free (uzbl.gui.icon);
1566 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1567 g_array_append_val (a, uzbl.state.uri);
1568 load_uri(uzbl.gui.web_view, a, NULL);
1569 g_array_free (a, TRUE);
1573 cmd_always_insert_mode() {
1574 uzbl.behave.insert_mode =
1575 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1581 g_object_set(G_OBJECT(uzbl.net.soup_session),
1582 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1586 cmd_max_conns_host() {
1587 g_object_set(G_OBJECT(uzbl.net.soup_session),
1588 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1593 soup_session_remove_feature
1594 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1595 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1596 /*g_free(uzbl.net.soup_logger);*/
1598 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1599 soup_session_add_feature(uzbl.net.soup_session,
1600 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1603 static WebKitWebSettings*
1605 return webkit_web_view_get_settings(uzbl.gui.web_view);
1610 WebKitWebSettings *ws = view_settings();
1611 if (uzbl.behave.font_size > 0) {
1612 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1615 if (uzbl.behave.monospace_size > 0) {
1616 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1617 uzbl.behave.monospace_size, NULL);
1619 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1620 uzbl.behave.font_size, NULL);
1626 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1630 cmd_disable_plugins() {
1631 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1632 !uzbl.behave.disable_plugins, NULL);
1636 cmd_disable_scripts() {
1637 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1638 !uzbl.behave.disable_scripts, NULL);
1642 cmd_minimum_font_size() {
1643 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1644 uzbl.behave.minimum_font_size, NULL);
1647 cmd_autoload_img() {
1648 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1649 uzbl.behave.autoload_img, NULL);
1654 cmd_autoshrink_img() {
1655 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1656 uzbl.behave.autoshrink_img, NULL);
1661 cmd_enable_spellcheck() {
1662 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1663 uzbl.behave.enable_spellcheck, NULL);
1667 cmd_enable_private() {
1668 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1669 uzbl.behave.enable_private, NULL);
1674 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1675 uzbl.behave.print_bg, NULL);
1680 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1681 uzbl.behave.style_uri, NULL);
1685 cmd_resizable_txt() {
1686 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1687 uzbl.behave.resizable_txt, NULL);
1691 cmd_default_encoding() {
1692 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1693 uzbl.behave.default_encoding, NULL);
1697 cmd_enforce_96dpi() {
1698 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1699 uzbl.behave.enforce_96dpi, NULL);
1703 cmd_caret_browsing() {
1704 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1705 uzbl.behave.caret_browsing, NULL);
1709 cmd_cookie_handler() {
1710 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1711 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1712 if ((g_strcmp0(split[0], "sh") == 0) ||
1713 (g_strcmp0(split[0], "spawn") == 0)) {
1714 g_free (uzbl.behave.cookie_handler);
1715 uzbl.behave.cookie_handler =
1716 g_strdup_printf("sync_%s %s", split[0], split[1]);
1723 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1728 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1733 if(uzbl.behave.inject_html) {
1734 webkit_web_view_load_html_string (uzbl.gui.web_view,
1735 uzbl.behave.inject_html, NULL);
1744 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1745 uzbl.behave.modmask = 0;
1747 if(uzbl.behave.modkey)
1748 g_free(uzbl.behave.modkey);
1749 uzbl.behave.modkey = buf;
1751 for (i = 0; modkeys[i].key != NULL; i++) {
1752 if (g_strrstr(buf, modkeys[i].key))
1753 uzbl.behave.modmask |= modkeys[i].mask;
1759 if (*uzbl.net.useragent == ' ') {
1760 g_free (uzbl.net.useragent);
1761 uzbl.net.useragent = NULL;
1763 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1764 uzbl.net.useragent, NULL);
1770 gtk_widget_ref(uzbl.gui.scrolled_win);
1771 gtk_widget_ref(uzbl.gui.mainbar);
1772 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1773 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1775 if(uzbl.behave.status_top) {
1776 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1777 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1780 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1781 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1783 gtk_widget_unref(uzbl.gui.scrolled_win);
1784 gtk_widget_unref(uzbl.gui.mainbar);
1785 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1790 set_var_value(gchar *name, gchar *val) {
1791 uzbl_cmdprop *c = NULL;
1795 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1796 if(!c->writeable) return TRUE;
1798 /* check for the variable type */
1799 if (c->type == TYPE_STR) {
1800 buf = expand(val, 0, FALSE);
1803 } else if(c->type == TYPE_INT) {
1804 int *ip = (int *)c->ptr;
1805 buf = expand(val, 0, FALSE);
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, FALSE);
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 /* set up gtk, gobject, variable defaults and other things that tests and other
2684 * external applications need to do anyhow */
2686 initialize(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 /* initialize hash table */
2701 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2703 uzbl.net.soup_session = webkit_get_default_session();
2704 uzbl.state.keycmd = g_string_new("");
2706 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2707 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2708 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2709 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2710 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2711 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2713 uzbl.gui.sbar.progress_s = g_strdup("=");
2714 uzbl.gui.sbar.progress_u = g_strdup("·");
2715 uzbl.gui.sbar.progress_w = 10;
2717 /* HTML mode defaults*/
2718 uzbl.behave.html_buffer = g_string_new("");
2719 uzbl.behave.html_endmarker = g_strdup(".");
2720 uzbl.behave.html_timeout = 60;
2721 uzbl.behave.base_url = g_strdup("http://invalid");
2723 /* default mode indicators */
2724 uzbl.behave.insert_indicator = g_strdup("I");
2725 uzbl.behave.cmd_indicator = g_strdup("C");
2727 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2728 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2729 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2730 uzbl.info.arch = ARCH;
2731 uzbl.info.commit = COMMIT;
2735 make_var_to_name_hash();
2737 uzbl.gui.scrolled_win = create_browser();
2740 #ifndef UZBL_LIBRARY
2743 main (int argc, char* argv[]) {
2744 initialize(argc, argv);
2746 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2750 /* initial packing */
2751 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2752 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2754 if (uzbl.state.socket_id) {
2755 uzbl.gui.plug = create_plug ();
2756 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2757 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2759 uzbl.gui.main_window = create_window ();
2760 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2761 gtk_widget_show_all (uzbl.gui.main_window);
2762 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2765 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2767 if (uzbl.state.verbose) {
2768 printf("Uzbl start location: %s\n", argv[0]);
2769 if (uzbl.state.socket_id)
2770 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2772 printf("window_id %i\n",(int) uzbl.xwin);
2773 printf("pid %i\n", getpid ());
2774 printf("name: %s\n", uzbl.state.instance_name);
2777 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2778 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2779 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2780 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2781 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2785 if (!uzbl.behave.show_status)
2786 gtk_widget_hide(uzbl.gui.mainbar);
2793 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2794 gboolean verbose_override = uzbl.state.verbose;
2796 if (verbose_override > uzbl.state.verbose)
2797 uzbl.state.verbose = verbose_override;
2800 set_var_value("uri", uri_override);
2801 g_free(uri_override);
2802 } else if (uzbl.state.uri)
2803 cmd_load_uri(uzbl.gui.web_view, NULL);
2808 return EXIT_SUCCESS;
2812 /* vi: set et ts=4: */