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)},
164 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
165 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
166 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
167 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
168 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
169 { "MSG", PTR_C(uzbl.gui.sbar.msg, STR, NULL)},
170 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
172 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
173 }, *n2v_p = var_name_to_ptr;
180 { "SHIFT", GDK_SHIFT_MASK }, // shift
181 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
182 { "CONTROL", GDK_CONTROL_MASK }, // control
183 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
184 { "MOD2", GDK_MOD2_MASK }, // 5th mod
185 { "MOD3", GDK_MOD3_MASK }, // 6th mod
186 { "MOD4", GDK_MOD4_MASK }, // 7th mod
187 { "MOD5", GDK_MOD5_MASK }, // 8th mod
188 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
189 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
190 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
191 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
192 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
193 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
194 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
195 { "META", GDK_META_MASK }, // meta (since 2.10)
200 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
201 * for quick access */
203 make_var_to_name_hash() {
204 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
206 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
211 /* --- UTILITY FUNCTIONS --- */
213 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
215 get_exp_type(gchar *s) {
219 else if(*(s+1) == '{')
220 return EXP_BRACED_VAR;
221 else if(*(s+1) == '<')
224 return EXP_SIMPLE_VAR;
230 * recurse == 1: don't expand '@(command)@'
231 * recurse == 2: don't expand '@<java script>@'
234 expand(char *s, guint recurse, gboolean escape_markup) {
238 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
243 gchar *cmd_stdout = NULL;
245 GString *buf = g_string_new("");
246 GString *js_ret = g_string_new("");
251 g_string_append_c(buf, *++s);
256 etype = get_exp_type(s);
261 if( (vend = strpbrk(s, end_simple_var)) ||
262 (vend = strchr(s, '\0')) ) {
263 strncpy(ret, s, vend-s);
269 if( (vend = strchr(s, upto)) ||
270 (vend = strchr(s, '\0')) ) {
271 strncpy(ret, s, vend-s);
277 strcpy(str_end, ")@");
279 if( (vend = strstr(s, str_end)) ||
280 (vend = strchr(s, '\0')) ) {
281 strncpy(ret, s, vend-s);
287 strcpy(str_end, ">@");
289 if( (vend = strstr(s, str_end)) ||
290 (vend = strchr(s, '\0')) ) {
291 strncpy(ret, s, vend-s);
297 if(etype == EXP_SIMPLE_VAR ||
298 etype == EXP_BRACED_VAR) {
299 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
300 if(c->type == TYPE_STR && *c->ptr != NULL) {
302 char *b = g_markup_escape_text((gchar *)*c->ptr,
303 strlen((gchar *)*c->ptr));
304 g_string_append(buf, b);
307 g_string_append(buf, (gchar *)*c->ptr);
309 } else if(c->type == TYPE_INT) {
310 char *b = itos((uintptr_t)*c->ptr);
311 g_string_append(buf, b);
316 if(etype == EXP_SIMPLE_VAR)
321 else if(recurse != 1 &&
323 mycmd = expand(ret, 1, escape_markup);
324 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
328 g_printerr("error on running command: %s\n", err->message);
331 else if (*cmd_stdout) {
332 int len = strlen(cmd_stdout);
334 if(cmd_stdout[len-1] == '\n')
335 cmd_stdout[--len] = 0; /* strip trailing newline */
338 char *b = g_markup_escape_text(cmd_stdout, len);
339 g_string_append(buf, b);
342 g_string_append(buf, cmd_stdout);
348 else if(recurse != 2 &&
350 mycmd = expand(ret, 2, escape_markup);
351 eval_js(uzbl.gui.web_view, mycmd, js_ret);
356 char *b = g_markup_escape_text(js_ret->str,
357 strlen(js_ret->str));
358 g_string_append(buf, b);
361 g_string_append(buf, js_ret->str);
363 g_string_free(js_ret, TRUE);
364 js_ret = g_string_new("");
371 g_string_append_c(buf, *s);
376 g_string_free(js_ret, TRUE);
377 return g_string_free(buf, FALSE);
384 snprintf(tmp, sizeof(tmp), "%i", val);
385 return g_strdup(tmp);
389 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
392 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
395 str_replace (const char* search, const char* replace, const char* string) {
399 buf = g_strsplit (string, search, -1);
400 ret = g_strjoinv (replace, buf);
401 g_strfreev(buf); // somebody said this segfaults
407 read_file_by_line (gchar *path) {
408 GIOChannel *chan = NULL;
409 gchar *readbuf = NULL;
411 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
414 chan = g_io_channel_new_file(path, "r", NULL);
417 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
418 const gchar* val = g_strdup (readbuf);
419 g_array_append_val (lines, val);
424 g_io_channel_unref (chan);
426 fprintf(stderr, "File '%s' not be read.\n", path);
433 gchar* parseenv (char* string) {
434 extern char** environ;
435 gchar* tmpstr = NULL;
439 while (environ[i] != NULL) {
440 gchar** env = g_strsplit (environ[i], "=", 2);
441 gchar* envname = g_strconcat ("$", env[0], NULL);
443 if (g_strrstr (string, envname) != NULL) {
444 tmpstr = g_strdup(string);
446 string = str_replace(envname, env[1], tmpstr);
451 g_strfreev (env); // somebody said this breaks uzbl
459 setup_signal(int signr, sigfunc *shandler) {
460 struct sigaction nh, oh;
462 nh.sa_handler = shandler;
463 sigemptyset(&nh.sa_mask);
466 if(sigaction(signr, &nh, &oh) < 0)
474 if (uzbl.behave.fifo_dir)
475 unlink (uzbl.comm.fifo_path);
476 if (uzbl.behave.socket_dir)
477 unlink (uzbl.comm.socket_path);
479 g_free(uzbl.state.executable_path);
480 g_string_free(uzbl.state.keycmd, TRUE);
481 g_hash_table_destroy(uzbl.bindings);
482 g_hash_table_destroy(uzbl.behave.commands);
485 /* used for html_mode_timeout
486 * be sure to extend this function to use
487 * more timers if needed in other places
490 set_timeout(int seconds) {
492 memset(&t, 0, sizeof t);
494 t.it_value.tv_sec = seconds;
495 t.it_value.tv_usec = 0;
496 setitimer(ITIMER_REAL, &t, NULL);
499 /* --- SIGNAL HANDLER --- */
502 catch_sigterm(int s) {
508 catch_sigint(int s) {
518 set_var_value("mode", "0");
523 /* --- CALLBACKS --- */
526 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
529 (void) navigation_action;
530 (void) policy_decision;
532 const gchar* uri = webkit_network_request_get_uri (request);
533 if (uzbl.state.verbose)
534 printf("New window requested -> %s \n", uri);
535 new_window_load_uri(uri);
540 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
545 /* If we can display it, let's display it... */
546 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
547 webkit_web_policy_decision_use (policy_decision);
551 /* ...everything we can't displayed is downloaded */
552 webkit_web_policy_decision_download (policy_decision);
557 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
561 if (uzbl.state.selected_url != NULL) {
562 if (uzbl.state.verbose)
563 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
564 new_window_load_uri(uzbl.state.selected_url);
566 if (uzbl.state.verbose)
567 printf("New web view -> %s\n","Nothing to open, exiting");
573 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
576 if (uzbl.behave.download_handler) {
577 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
578 if (uzbl.state.verbose)
579 printf("Download -> %s\n",uri);
580 /* if urls not escaped, we may have to escape and quote uri before this call */
581 run_handler(uzbl.behave.download_handler, uri);
586 /* scroll a bar in a given direction */
588 scroll (GtkAdjustment* bar, GArray *argv) {
592 gdouble page_size = gtk_adjustment_get_page_size(bar);
593 gdouble value = gtk_adjustment_get_value(bar);
594 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
597 value += page_size * amount * 0.01;
601 max_value = gtk_adjustment_get_upper(bar) - page_size;
603 if (value > max_value)
604 value = max_value; /* don't scroll past the end of the page */
606 gtk_adjustment_set_value (bar, value);
610 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
611 (void) page; (void) argv; (void) result;
612 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
616 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) argv; (void) result;
618 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
619 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
623 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
624 (void) page; (void) result;
625 scroll(uzbl.gui.bar_v, argv);
629 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
630 (void) page; (void) result;
631 scroll(uzbl.gui.bar_h, argv);
636 if (!uzbl.behave.show_status) {
637 gtk_widget_hide(uzbl.gui.mainbar);
639 gtk_widget_show(uzbl.gui.mainbar);
645 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
650 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
654 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
659 if (uzbl.behave.show_status) {
660 gtk_widget_hide(uzbl.gui.mainbar);
662 gtk_widget_show(uzbl.gui.mainbar);
664 uzbl.behave.show_status = !uzbl.behave.show_status;
669 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
673 //Set selected_url state variable
674 g_free(uzbl.state.selected_url);
675 uzbl.state.selected_url = NULL;
677 uzbl.state.selected_url = g_strdup(link);
683 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
686 const gchar *title = webkit_web_view_get_title(web_view);
687 if (uzbl.gui.main_title)
688 g_free (uzbl.gui.main_title);
689 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
694 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
697 uzbl.gui.sbar.load_progress = progress;
699 g_free(uzbl.gui.sbar.progress_bar);
700 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
706 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
710 if (uzbl.behave.load_finish_handler)
711 run_handler(uzbl.behave.load_finish_handler, "");
715 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
719 uzbl.gui.sbar.load_progress = 0;
720 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
721 if (uzbl.behave.load_start_handler)
722 run_handler(uzbl.behave.load_start_handler, "");
726 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
729 g_free (uzbl.state.uri);
730 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
731 uzbl.state.uri = g_string_free (newuri, FALSE);
732 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
733 set_insert_mode(uzbl.behave.always_insert_mode);
736 if (uzbl.behave.load_commit_handler)
737 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
741 destroy_cb (GtkWidget* widget, gpointer data) {
749 if (uzbl.behave.history_handler) {
751 struct tm * timeinfo;
754 timeinfo = localtime ( &rawtime );
755 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
756 run_handler(uzbl.behave.history_handler, date);
761 /* VIEW funcs (little webkit wrappers) */
762 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
764 VIEWFUNC(reload_bypass_cache)
765 VIEWFUNC(stop_loading)
772 /* -- command to callback/function map for things we cannot attach to any signals */
773 static struct {char *key; CommandInfo value;} cmdlist[] =
774 { /* key function no_split */
775 { "back", {view_go_back, 0} },
776 { "forward", {view_go_forward, 0} },
777 { "scroll_vert", {scroll_vert, 0} },
778 { "scroll_horz", {scroll_horz, 0} },
779 { "scroll_begin", {scroll_begin, 0} },
780 { "scroll_end", {scroll_end, 0} },
781 { "reload", {view_reload, 0}, },
782 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
783 { "stop", {view_stop_loading, 0}, },
784 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
785 { "zoom_out", {view_zoom_out, 0}, },
786 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
787 { "uri", {load_uri, TRUE} },
788 { "js", {run_js, TRUE} },
789 { "script", {run_external_js, 0} },
790 { "toggle_status", {toggle_status_cb, 0} },
791 { "spawn", {spawn, 0} },
792 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
793 { "sh", {spawn_sh, 0} },
794 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
795 { "exit", {close_uzbl, 0} },
796 { "search", {search_forward_text, TRUE} },
797 { "search_reverse", {search_reverse_text, TRUE} },
798 { "dehilight", {dehilight, 0} },
799 { "toggle_insert_mode", {toggle_insert_mode, 0} },
800 { "set", {set_var, TRUE} },
801 //{ "get", {get_var, TRUE} },
802 { "bind", {act_bind, TRUE} },
803 { "dump_config", {act_dump_config, 0} },
804 { "keycmd", {keycmd, TRUE} },
805 { "keycmd_nl", {keycmd_nl, TRUE} },
806 { "keycmd_bs", {keycmd_bs, 0} },
807 { "chain", {chain, 0} },
808 { "print", {print, TRUE} }
815 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
817 for (i = 0; i < LENGTH(cmdlist); i++)
818 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
821 /* -- CORE FUNCTIONS -- */
824 free_action(gpointer act) {
825 Action *action = (Action*)act;
826 g_free(action->name);
828 g_free(action->param);
833 new_action(const gchar *name, const gchar *param) {
834 Action *action = g_new(Action, 1);
836 action->name = g_strdup(name);
838 action->param = g_strdup(param);
840 action->param = NULL;
846 file_exists (const char * filename) {
847 return (access(filename, F_OK) == 0);
851 set_var(WebKitWebView *page, GArray *argv, GString *result) {
852 (void) page; (void) result;
853 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
854 if (split[0] != NULL) {
855 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
856 set_var_value(g_strstrip(split[0]), value);
863 print(WebKitWebView *page, GArray *argv, GString *result) {
864 (void) page; (void) result;
867 buf = expand(argv_idx(argv, 0), 0, FALSE);
868 g_string_assign(result, buf);
873 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
874 (void) page; (void) result;
875 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
876 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
877 add_binding(g_strstrip(split[0]), value);
888 void set_insert_mode(gboolean mode) {
889 uzbl.behave.insert_mode = mode;
890 uzbl.gui.sbar.mode_indicator = (mode ?
891 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
895 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
896 (void) page; (void) result;
898 if (argv_idx(argv, 0)) {
899 if (strcmp (argv_idx(argv, 0), "0") == 0) {
900 set_insert_mode(FALSE);
902 set_insert_mode(TRUE);
905 set_insert_mode( !uzbl.behave.insert_mode );
912 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
915 if (argv_idx(argv, 0)) {
916 GString* newuri = g_string_new (argv_idx(argv, 0));
917 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
918 run_js(web_view, argv, NULL);
921 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
922 g_string_prepend (newuri, "http://");
923 /* if we do handle cookies, ask our handler for them */
924 webkit_web_view_load_uri (web_view, newuri->str);
925 g_string_free (newuri, TRUE);
933 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
934 size_t argumentCount, const JSValueRef arguments[],
935 JSValueRef* exception) {
940 JSStringRef js_result_string;
941 GString *result = g_string_new("");
943 if (argumentCount >= 1) {
944 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
945 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
946 char ctl_line[arg_size];
947 JSStringGetUTF8CString(arg, ctl_line, arg_size);
949 parse_cmd_line(ctl_line, result);
951 JSStringRelease(arg);
953 js_result_string = JSStringCreateWithUTF8CString(result->str);
955 g_string_free(result, TRUE);
957 return JSValueMakeString(ctx, js_result_string);
960 static JSStaticFunction js_static_functions[] = {
961 {"run", js_run_command, kJSPropertyAttributeNone},
966 /* This function creates the class and its definition, only once */
967 if (!uzbl.js.initialized) {
968 /* it would be pretty cool to make this dynamic */
969 uzbl.js.classdef = kJSClassDefinitionEmpty;
970 uzbl.js.classdef.staticFunctions = js_static_functions;
972 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
978 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
979 WebKitWebFrame *frame;
980 JSGlobalContextRef context;
981 JSObjectRef globalobject;
982 JSStringRef var_name;
984 JSStringRef js_script;
985 JSValueRef js_result;
986 JSStringRef js_result_string;
987 size_t js_result_size;
991 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
992 context = webkit_web_frame_get_global_context(frame);
993 globalobject = JSContextGetGlobalObject(context);
995 /* uzbl javascript namespace */
996 var_name = JSStringCreateWithUTF8CString("Uzbl");
997 JSObjectSetProperty(context, globalobject, var_name,
998 JSObjectMake(context, uzbl.js.classref, NULL),
999 kJSClassAttributeNone, NULL);
1001 /* evaluate the script and get return value*/
1002 js_script = JSStringCreateWithUTF8CString(script);
1003 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1004 if (js_result && !JSValueIsUndefined(context, js_result)) {
1005 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1006 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1008 if (js_result_size) {
1009 char js_result_utf8[js_result_size];
1010 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1011 g_string_assign(result, js_result_utf8);
1014 JSStringRelease(js_result_string);
1018 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1020 JSStringRelease(var_name);
1021 JSStringRelease(js_script);
1025 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1027 if (argv_idx(argv, 0))
1028 eval_js(web_view, argv_idx(argv, 0), result);
1032 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1034 if (argv_idx(argv, 0)) {
1035 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1040 while ((line = g_array_index(lines, gchar*, i))) {
1042 js = g_strdup (line);
1044 gchar* newjs = g_strconcat (js, line, NULL);
1051 if (uzbl.state.verbose)
1052 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1054 if (argv_idx (argv, 1)) {
1055 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1059 eval_js (web_view, js, result);
1061 g_array_free (lines, TRUE);
1066 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1067 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1068 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1069 webkit_web_view_unmark_text_matches (page);
1070 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1071 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1075 if (uzbl.state.searchtx) {
1076 if (uzbl.state.verbose)
1077 printf ("Searching: %s\n", uzbl.state.searchtx);
1078 webkit_web_view_set_highlight_text_matches (page, TRUE);
1079 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1084 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1086 search_text(page, argv, TRUE);
1090 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1092 search_text(page, argv, FALSE);
1096 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1097 (void) argv; (void) result;
1098 webkit_web_view_set_highlight_text_matches (page, FALSE);
1103 new_window_load_uri (const gchar * uri) {
1104 GString* to_execute = g_string_new ("");
1105 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1107 for (i = 0; entries[i].long_name != NULL; i++) {
1108 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1109 gchar** str = (gchar**)entries[i].arg_data;
1111 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1115 if (uzbl.state.verbose)
1116 printf("\n%s\n", to_execute->str);
1117 g_spawn_command_line_async (to_execute->str, NULL);
1118 g_string_free (to_execute, TRUE);
1122 chain (WebKitWebView *page, GArray *argv, GString *result) {
1123 (void) page; (void) result;
1125 gchar **parts = NULL;
1127 while ((a = argv_idx(argv, i++))) {
1128 parts = g_strsplit (a, " ", 2);
1129 parse_command(parts[0], parts[1], result);
1135 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1139 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1145 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1149 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1155 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1160 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1162 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1167 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1174 /* --Statusbar functions-- */
1176 build_progressbar_ascii(int percent) {
1177 int width=uzbl.gui.sbar.progress_w;
1180 GString *bar = g_string_new("");
1182 l = (double)percent*((double)width/100.);
1183 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1185 for(i=0; i<(int)l; i++)
1186 g_string_append(bar, uzbl.gui.sbar.progress_s);
1189 g_string_append(bar, uzbl.gui.sbar.progress_u);
1191 return g_string_free(bar, FALSE);
1196 const GScannerConfig scan_config = {
1199 ) /* cset_skip_characters */,
1204 ) /* cset_identifier_first */,
1211 ) /* cset_identifier_nth */,
1212 ( "" ) /* cpair_comment_single */,
1214 TRUE /* case_sensitive */,
1216 FALSE /* skip_comment_multi */,
1217 FALSE /* skip_comment_single */,
1218 FALSE /* scan_comment_multi */,
1219 TRUE /* scan_identifier */,
1220 TRUE /* scan_identifier_1char */,
1221 FALSE /* scan_identifier_NULL */,
1222 TRUE /* scan_symbols */,
1223 FALSE /* scan_binary */,
1224 FALSE /* scan_octal */,
1225 FALSE /* scan_float */,
1226 FALSE /* scan_hex */,
1227 FALSE /* scan_hex_dollar */,
1228 FALSE /* scan_string_sq */,
1229 FALSE /* scan_string_dq */,
1230 TRUE /* numbers_2_int */,
1231 FALSE /* int_2_float */,
1232 FALSE /* identifier_2_string */,
1233 FALSE /* char_2_token */,
1234 FALSE /* symbol_2_token */,
1235 TRUE /* scope_0_fallback */,
1240 uzbl.scan = g_scanner_new(&scan_config);
1241 while(symp->symbol_name) {
1242 g_scanner_scope_add_symbol(uzbl.scan, 0,
1244 GINT_TO_POINTER(symp->symbol_token));
1250 expand_template(const char *template, gboolean escape_markup) {
1251 if(!template) return NULL;
1253 GTokenType token = G_TOKEN_NONE;
1254 GString *ret = g_string_new("");
1258 g_scanner_input_text(uzbl.scan, template, strlen(template));
1259 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1260 token = g_scanner_get_next_token(uzbl.scan);
1262 if(token == G_TOKEN_SYMBOL) {
1263 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1267 buf = uzbl.state.keycmd->str?
1268 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1269 g_string_append(ret, buf);
1273 g_string_append(ret, uzbl.state.keycmd->str?
1274 uzbl.state.keycmd->str:g_strdup(""));
1280 else if(token == G_TOKEN_INT) {
1281 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1282 g_string_append(ret, buf);
1285 else if(token == G_TOKEN_IDENTIFIER) {
1286 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1288 else if(token == G_TOKEN_CHAR) {
1289 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1293 return g_string_free(ret, FALSE);
1295 /* --End Statusbar functions-- */
1298 sharg_append(GArray *a, const gchar *str) {
1299 const gchar *s = (str ? str : "");
1300 g_array_append_val(a, s);
1303 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1305 run_command (const gchar *command, const guint npre, const gchar **args,
1306 const gboolean sync, char **output_stdout) {
1307 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1310 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311 gchar *pid = itos(getpid());
1312 gchar *xwin = itos(uzbl.xwin);
1314 sharg_append(a, command);
1315 for (i = 0; i < npre; i++) /* add n args before the default vars */
1316 sharg_append(a, args[i]);
1317 sharg_append(a, uzbl.state.config_file);
1318 sharg_append(a, pid);
1319 sharg_append(a, xwin);
1320 sharg_append(a, uzbl.comm.fifo_path);
1321 sharg_append(a, uzbl.comm.socket_path);
1322 sharg_append(a, uzbl.state.uri);
1323 sharg_append(a, uzbl.gui.main_title);
1325 for (i = npre; i < g_strv_length((gchar**)args); i++)
1326 sharg_append(a, args[i]);
1330 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1332 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1333 NULL, NULL, output_stdout, NULL, NULL, &err);
1334 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1335 NULL, NULL, NULL, &err);
1337 if (uzbl.state.verbose) {
1338 GString *s = g_string_new("spawned:");
1339 for (i = 0; i < (a->len); i++) {
1340 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1341 g_string_append_printf(s, " %s", qarg);
1344 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1345 printf("%s\n", s->str);
1346 g_string_free(s, TRUE);
1348 printf("Stdout: %s\n", *output_stdout);
1352 g_printerr("error on run_command: %s\n", err->message);
1357 g_array_free (a, TRUE);
1362 split_quoted(const gchar* src, const gboolean unquote) {
1363 /* split on unquoted space, return array of strings;
1364 remove a layer of quotes and backslashes if unquote */
1365 if (!src) return NULL;
1367 gboolean dq = FALSE;
1368 gboolean sq = FALSE;
1369 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1370 GString *s = g_string_new ("");
1374 for (p = src; *p != '\0'; p++) {
1375 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1376 else if (*p == '\\') { g_string_append_c(s, *p++);
1377 g_string_append_c(s, *p); }
1378 else if ((*p == '"') && unquote && !sq) dq = !dq;
1379 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1381 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1382 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1384 else if ((*p == ' ') && !dq && !sq) {
1385 dup = g_strdup(s->str);
1386 g_array_append_val(a, dup);
1387 g_string_truncate(s, 0);
1388 } else g_string_append_c(s, *p);
1390 dup = g_strdup(s->str);
1391 g_array_append_val(a, dup);
1392 ret = (gchar**)a->data;
1393 g_array_free (a, FALSE);
1394 g_string_free (s, TRUE);
1399 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1400 (void)web_view; (void)result;
1401 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1402 if (argv_idx(argv, 0))
1403 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1407 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1408 (void)web_view; (void)result;
1410 if (argv_idx(argv, 0))
1411 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1412 TRUE, &uzbl.comm.sync_stdout);
1416 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1417 (void)web_view; (void)result;
1418 if (!uzbl.behave.shell_cmd) {
1419 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1424 gchar *spacer = g_strdup("");
1425 g_array_insert_val(argv, 1, spacer);
1426 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1428 for (i = 1; i < g_strv_length(cmd); i++)
1429 g_array_prepend_val(argv, cmd[i]);
1431 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1437 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1438 (void)web_view; (void)result;
1439 if (!uzbl.behave.shell_cmd) {
1440 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1445 gchar *spacer = g_strdup("");
1446 g_array_insert_val(argv, 1, spacer);
1447 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1449 for (i = 1; i < g_strv_length(cmd); i++)
1450 g_array_prepend_val(argv, cmd[i]);
1452 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1453 TRUE, &uzbl.comm.sync_stdout);
1459 parse_command(const char *cmd, const char *param, GString *result) {
1462 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1464 gchar **par = split_quoted(param, TRUE);
1465 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1467 if (c->no_split) { /* don't split */
1468 sharg_append(a, param);
1470 for (i = 0; i < g_strv_length(par); i++)
1471 sharg_append(a, par[i]);
1474 if (result == NULL) {
1475 GString *result_print = g_string_new("");
1477 c->function(uzbl.gui.web_view, a, result_print);
1478 if (result_print->len)
1479 printf("%*s\n", result_print->len, result_print->str);
1481 g_string_free(result_print, TRUE);
1483 c->function(uzbl.gui.web_view, a, result);
1486 g_array_free (a, TRUE);
1489 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1496 if(*uzbl.net.proxy_url == ' '
1497 || uzbl.net.proxy_url == NULL) {
1498 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1499 (GType) SOUP_SESSION_PROXY_URI);
1502 suri = soup_uri_new(uzbl.net.proxy_url);
1503 g_object_set(G_OBJECT(uzbl.net.soup_session),
1504 SOUP_SESSION_PROXY_URI,
1506 soup_uri_free(suri);
1513 if(file_exists(uzbl.gui.icon)) {
1514 if (uzbl.gui.main_window)
1515 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1517 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1519 g_free (uzbl.gui.icon);
1524 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1525 g_array_append_val (a, uzbl.state.uri);
1526 load_uri(uzbl.gui.web_view, a, NULL);
1527 g_array_free (a, TRUE);
1531 cmd_always_insert_mode() {
1532 set_insert_mode(uzbl.behave.always_insert_mode);
1538 g_object_set(G_OBJECT(uzbl.net.soup_session),
1539 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1543 cmd_max_conns_host() {
1544 g_object_set(G_OBJECT(uzbl.net.soup_session),
1545 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1550 soup_session_remove_feature
1551 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1552 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1553 /*g_free(uzbl.net.soup_logger);*/
1555 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1556 soup_session_add_feature(uzbl.net.soup_session,
1557 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1560 static WebKitWebSettings*
1562 return webkit_web_view_get_settings(uzbl.gui.web_view);
1567 WebKitWebSettings *ws = view_settings();
1568 if (uzbl.behave.font_size > 0) {
1569 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1572 if (uzbl.behave.monospace_size > 0) {
1573 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1574 uzbl.behave.monospace_size, NULL);
1576 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1577 uzbl.behave.font_size, NULL);
1583 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1587 cmd_disable_plugins() {
1588 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1589 !uzbl.behave.disable_plugins, NULL);
1593 cmd_disable_scripts() {
1594 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1595 !uzbl.behave.disable_scripts, NULL);
1599 cmd_minimum_font_size() {
1600 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1601 uzbl.behave.minimum_font_size, NULL);
1604 cmd_autoload_img() {
1605 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1606 uzbl.behave.autoload_img, NULL);
1611 cmd_autoshrink_img() {
1612 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1613 uzbl.behave.autoshrink_img, NULL);
1618 cmd_enable_spellcheck() {
1619 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1620 uzbl.behave.enable_spellcheck, NULL);
1624 cmd_enable_private() {
1625 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1626 uzbl.behave.enable_private, NULL);
1631 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1632 uzbl.behave.print_bg, NULL);
1637 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1638 uzbl.behave.style_uri, NULL);
1642 cmd_resizable_txt() {
1643 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1644 uzbl.behave.resizable_txt, NULL);
1648 cmd_default_encoding() {
1649 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1650 uzbl.behave.default_encoding, NULL);
1654 cmd_enforce_96dpi() {
1655 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1656 uzbl.behave.enforce_96dpi, NULL);
1660 cmd_caret_browsing() {
1661 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1662 uzbl.behave.caret_browsing, NULL);
1666 cmd_cookie_handler() {
1667 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1668 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1669 if ((g_strcmp0(split[0], "sh") == 0) ||
1670 (g_strcmp0(split[0], "spawn") == 0)) {
1671 g_free (uzbl.behave.cookie_handler);
1672 uzbl.behave.cookie_handler =
1673 g_strdup_printf("sync_%s %s", split[0], split[1]);
1680 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1685 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1690 if(uzbl.behave.inject_html) {
1691 webkit_web_view_load_html_string (uzbl.gui.web_view,
1692 uzbl.behave.inject_html, NULL);
1701 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1702 uzbl.behave.modmask = 0;
1704 if(uzbl.behave.modkey)
1705 g_free(uzbl.behave.modkey);
1706 uzbl.behave.modkey = buf;
1708 for (i = 0; modkeys[i].key != NULL; i++) {
1709 if (g_strrstr(buf, modkeys[i].key))
1710 uzbl.behave.modmask |= modkeys[i].mask;
1716 if (*uzbl.net.useragent == ' ') {
1717 g_free (uzbl.net.useragent);
1718 uzbl.net.useragent = NULL;
1720 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1721 uzbl.net.useragent, NULL);
1727 gtk_widget_ref(uzbl.gui.scrolled_win);
1728 gtk_widget_ref(uzbl.gui.mainbar);
1729 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1730 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1732 if(uzbl.behave.status_top) {
1733 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1734 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1737 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1738 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1740 gtk_widget_unref(uzbl.gui.scrolled_win);
1741 gtk_widget_unref(uzbl.gui.mainbar);
1742 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1747 set_var_value(gchar *name, gchar *val) {
1748 uzbl_cmdprop *c = NULL;
1752 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1753 if(!c->writeable) return TRUE;
1755 /* check for the variable type */
1756 if (c->type == TYPE_STR) {
1757 buf = expand(val, 0, FALSE);
1760 } else if(c->type == TYPE_INT) {
1761 int *ip = (int *)c->ptr;
1762 buf = expand(val, 0, FALSE);
1763 *ip = (int)strtoul(buf, &endp, 10);
1765 } else if (c->type == TYPE_FLOAT) {
1766 float *fp = (float *)c->ptr;
1767 buf = expand(val, 0, FALSE);
1768 *fp = strtod(buf, &endp);
1772 /* invoke a command specific function */
1773 if(c->func) c->func();
1780 Behaviour *b = &uzbl.behave;
1782 if(b->html_buffer->str) {
1783 webkit_web_view_load_html_string (uzbl.gui.web_view,
1784 b->html_buffer->str, b->base_url);
1785 g_string_free(b->html_buffer, TRUE);
1786 b->html_buffer = g_string_new("");
1790 enum {M_CMD, M_HTML};
1792 parse_cmd_line(const char *ctl_line, GString *result) {
1793 Behaviour *b = &uzbl.behave;
1796 if(b->mode == M_HTML) {
1797 len = strlen(b->html_endmarker);
1798 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1799 if(len == strlen(ctl_line)-1 &&
1800 !strncmp(b->html_endmarker, ctl_line, len)) {
1802 set_var_value("mode", "0");
1807 set_timeout(b->html_timeout);
1808 g_string_append(b->html_buffer, ctl_line);
1811 else if((ctl_line[0] == '#') /* Comments */
1812 || (ctl_line[0] == ' ')
1813 || (ctl_line[0] == '\n'))
1814 ; /* ignore these lines */
1815 else { /* parse a command */
1817 gchar **tokens = NULL;
1818 len = strlen(ctl_line);
1820 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1821 ctlstrip = g_strndup(ctl_line, len - 1);
1822 else ctlstrip = g_strdup(ctl_line);
1824 tokens = g_strsplit(ctlstrip, " ", 2);
1825 parse_command(tokens[0], tokens[1], result);
1832 build_stream_name(int type, const gchar* dir) {
1833 State *s = &uzbl.state;
1837 str = g_strdup_printf
1838 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1839 } else if (type == SOCKET) {
1840 str = g_strdup_printf
1841 ("%s/uzbl_socket_%s", dir, s->instance_name);
1847 control_fifo(GIOChannel *gio, GIOCondition condition) {
1848 if (uzbl.state.verbose)
1849 printf("triggered\n");
1854 if (condition & G_IO_HUP)
1855 g_error ("Fifo: Read end of pipe died!\n");
1858 g_error ("Fifo: GIOChannel broke\n");
1860 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1861 if (ret == G_IO_STATUS_ERROR) {
1862 g_error ("Fifo: Error reading: %s\n", err->message);
1866 parse_cmd_line(ctl_line, NULL);
1873 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1874 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1875 if (unlink(uzbl.comm.fifo_path) == -1)
1876 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1877 g_free(uzbl.comm.fifo_path);
1878 uzbl.comm.fifo_path = NULL;
1881 if (*dir == ' ') { /* space unsets the variable */
1886 GIOChannel *chan = NULL;
1887 GError *error = NULL;
1888 gchar *path = build_stream_name(FIFO, dir);
1890 if (!file_exists(path)) {
1891 if (mkfifo (path, 0666) == 0) {
1892 // 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.
1893 chan = g_io_channel_new_file(path, "r+", &error);
1895 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1896 if (uzbl.state.verbose)
1897 printf ("init_fifo: created successfully as %s\n", path);
1898 uzbl.comm.fifo_path = path;
1900 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1901 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1902 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1903 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1905 /* if we got this far, there was an error; cleanup */
1906 if (error) g_error_free (error);
1913 control_stdin(GIOChannel *gio, GIOCondition condition) {
1915 gchar *ctl_line = NULL;
1918 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1919 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1922 parse_cmd_line(ctl_line, NULL);
1930 GIOChannel *chan = NULL;
1931 GError *error = NULL;
1933 chan = g_io_channel_unix_new(fileno(stdin));
1935 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1936 g_error ("Stdin: could not add watch\n");
1938 if (uzbl.state.verbose)
1939 printf ("Stdin: watch added successfully\n");
1942 g_error ("Stdin: Error while opening: %s\n", error->message);
1944 if (error) g_error_free (error);
1948 control_socket(GIOChannel *chan) {
1949 struct sockaddr_un remote;
1950 unsigned int t = sizeof(remote);
1952 GIOChannel *clientchan;
1954 clientsock = accept (g_io_channel_unix_get_fd(chan),
1955 (struct sockaddr *) &remote, &t);
1957 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1958 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1959 (GIOFunc) control_client_socket, clientchan);
1966 control_client_socket(GIOChannel *clientchan) {
1968 GString *result = g_string_new("");
1969 GError *error = NULL;
1973 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1974 if (ret == G_IO_STATUS_ERROR) {
1975 g_warning ("Error reading: %s\n", error->message);
1976 g_io_channel_shutdown(clientchan, TRUE, &error);
1978 } else if (ret == G_IO_STATUS_EOF) {
1979 /* shutdown and remove channel watch from main loop */
1980 g_io_channel_shutdown(clientchan, TRUE, &error);
1985 parse_cmd_line (ctl_line, result);
1986 g_string_append_c(result, '\n');
1987 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1989 if (ret == G_IO_STATUS_ERROR) {
1990 g_warning ("Error writing: %s", error->message);
1992 g_io_channel_flush(clientchan, &error);
1995 if (error) g_error_free (error);
1996 g_string_free(result, TRUE);
2002 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2003 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2004 if (unlink(uzbl.comm.socket_path) == -1)
2005 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2006 g_free(uzbl.comm.socket_path);
2007 uzbl.comm.socket_path = NULL;
2015 GIOChannel *chan = NULL;
2017 struct sockaddr_un local;
2018 gchar *path = build_stream_name(SOCKET, dir);
2020 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2022 local.sun_family = AF_UNIX;
2023 strcpy (local.sun_path, path);
2024 unlink (local.sun_path);
2026 len = strlen (local.sun_path) + sizeof (local.sun_family);
2027 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2028 if (uzbl.state.verbose)
2029 printf ("init_socket: opened in %s\n", path);
2032 if( (chan = g_io_channel_unix_new(sock)) ) {
2033 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2034 uzbl.comm.socket_path = path;
2037 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2039 /* if we got this far, there was an error; cleanup */
2046 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2047 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2049 // this function may be called very early when the templates are not set (yet), hence the checks
2051 update_title (void) {
2052 Behaviour *b = &uzbl.behave;
2055 if (b->show_status) {
2056 if (b->title_format_short) {
2057 parsed = expand_template(b->title_format_short, FALSE);
2058 parsed = expand(parsed, 0, FALSE);
2059 if (uzbl.gui.main_window)
2060 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2063 if (b->status_format) {
2064 parsed = expand_template(b->status_format, TRUE);
2065 parsed = expand(parsed, 0, TRUE);
2066 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2069 if (b->status_background) {
2071 gdk_color_parse (b->status_background, &color);
2072 //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)
2073 if (uzbl.gui.main_window)
2074 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2075 else if (uzbl.gui.plug)
2076 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2079 if (b->title_format_long) {
2080 parsed = expand_template(b->title_format_long, FALSE);
2081 parsed = expand(parsed, 0, FALSE);
2082 if (uzbl.gui.main_window)
2083 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2090 key_press_cb (GtkWidget* window, GdkEventKey* event)
2092 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2096 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2097 || 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)
2100 /* turn off insert mode (if always_insert_mode is not used) */
2101 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2102 set_insert_mode(uzbl.behave.always_insert_mode);
2107 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2110 if (event->keyval == GDK_Escape) {
2111 g_string_truncate(uzbl.state.keycmd, 0);
2113 dehilight(uzbl.gui.web_view, NULL, NULL);
2117 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2118 if (event->keyval == GDK_Insert) {
2120 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2121 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2123 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2126 g_string_append (uzbl.state.keycmd, str);
2133 if (event->keyval == GDK_BackSpace)
2134 keycmd_bs(NULL, NULL, NULL);
2136 gboolean key_ret = FALSE;
2137 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2139 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2141 run_keycmd(key_ret);
2143 if (key_ret) return (!uzbl.behave.insert_mode);
2148 run_keycmd(const gboolean key_ret) {
2149 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2151 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2152 g_string_truncate(uzbl.state.keycmd, 0);
2153 parse_command(act->name, act->param, NULL);
2157 /* try if it's an incremental keycmd or one that takes args, and run it */
2158 GString* short_keys = g_string_new ("");
2159 GString* short_keys_inc = g_string_new ("");
2161 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2162 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2163 g_string_assign(short_keys_inc, short_keys->str);
2164 g_string_append_c(short_keys, '_');
2165 g_string_append_c(short_keys_inc, '*');
2167 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2168 /* run normal cmds only if return was pressed */
2169 exec_paramcmd(act, i);
2170 g_string_truncate(uzbl.state.keycmd, 0);
2172 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2173 if (key_ret) /* just quit the incremental command on return */
2174 g_string_truncate(uzbl.state.keycmd, 0);
2175 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2179 g_string_truncate(short_keys, short_keys->len - 1);
2181 g_string_free (short_keys, TRUE);
2182 g_string_free (short_keys_inc, TRUE);
2186 exec_paramcmd(const Action *act, const guint i) {
2187 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2188 GString *actionname = g_string_new ("");
2189 GString *actionparam = g_string_new ("");
2190 g_string_erase (parampart, 0, i+1);
2192 g_string_printf (actionname, act->name, parampart->str);
2194 g_string_printf (actionparam, act->param, parampart->str);
2195 parse_command(actionname->str, actionparam->str, NULL);
2196 g_string_free(actionname, TRUE);
2197 g_string_free(actionparam, TRUE);
2198 g_string_free(parampart, TRUE);
2206 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2207 //main_window_ref = g_object_ref(scrolled_window);
2208 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
2210 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2211 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2213 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2214 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2215 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2216 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2217 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2218 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2219 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2220 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2221 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2222 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2223 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2225 return scrolled_window;
2232 g->mainbar = gtk_hbox_new (FALSE, 0);
2234 /* keep a reference to the bar so we can re-pack it at runtime*/
2235 //sbar_ref = g_object_ref(g->mainbar);
2237 g->mainbar_label = gtk_label_new ("");
2238 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2239 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2240 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2241 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2242 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2243 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2248 GtkWidget* create_window () {
2249 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2250 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2251 gtk_widget_set_name (window, "Uzbl browser");
2252 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2253 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2259 GtkPlug* create_plug () {
2260 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2261 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2262 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2269 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2271 If actname is one that calls an external command, this function will inject
2272 newargs in front of the user-provided args in that command line. They will
2273 come become after the body of the script (in sh) or after the name of
2274 the command to execute (in spawn).
2275 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2276 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2278 The return value consist of two strings: the action (sh, ...) and its args.
2280 If act is not one that calls an external command, then the given action merely
2283 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2284 gchar *actdup = g_strdup(actname);
2285 g_array_append_val(rets, actdup);
2287 if ((g_strcmp0(actname, "spawn") == 0) ||
2288 (g_strcmp0(actname, "sh") == 0) ||
2289 (g_strcmp0(actname, "sync_spawn") == 0) ||
2290 (g_strcmp0(actname, "sync_sh") == 0)) {
2292 GString *a = g_string_new("");
2293 gchar **spawnparts = split_quoted(origargs, FALSE);
2294 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2295 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2297 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2298 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2300 g_array_append_val(rets, a->str);
2301 g_string_free(a, FALSE);
2302 g_strfreev(spawnparts);
2304 gchar *origdup = g_strdup(origargs);
2305 g_array_append_val(rets, origdup);
2307 return (gchar**)g_array_free(rets, FALSE);
2311 run_handler (const gchar *act, const gchar *args) {
2312 /* Consider this code a temporary hack to make the handlers usable.
2313 In practice, all this splicing, injection, and reconstruction is
2314 inefficient, annoying and hard to manage. Potential pitfalls arise
2315 when the handler specific args 1) are not quoted (the handler
2316 callbacks should take care of this) 2) are quoted but interfere
2317 with the users' own quotation. A more ideal solution is
2318 to refactor parse_command so that it doesn't just take a string
2319 and execute it; rather than that, we should have a function which
2320 returns the argument vector parsed from the string. This vector
2321 could be modified (e.g. insert additional args into it) before
2322 passing it to the next function that actually executes it. Though
2323 it still isn't perfect for chain actions.. will reconsider & re-
2324 factor when I have the time. -duc */
2326 char **parts = g_strsplit(act, " ", 2);
2328 if (g_strcmp0(parts[0], "chain") == 0) {
2329 GString *newargs = g_string_new("");
2330 gchar **chainparts = split_quoted(parts[1], FALSE);
2332 /* for every argument in the chain, inject the handler args
2333 and make sure the new parts are wrapped in quotes */
2334 gchar **cp = chainparts;
2336 gchar *quotless = NULL;
2337 gchar **spliced_quotless = NULL; // sigh -_-;
2338 gchar **inpart = NULL;
2341 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2343 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2344 } else quotless = g_strdup(*cp);
2346 spliced_quotless = g_strsplit(quotless, " ", 2);
2347 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2348 g_strfreev(spliced_quotless);
2350 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2356 parse_command(parts[0], &(newargs->str[1]), NULL);
2357 g_string_free(newargs, TRUE);
2358 g_strfreev(chainparts);
2361 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2362 parse_command(inparts[0], inparts[1], NULL);
2370 add_binding (const gchar *key, const gchar *act) {
2371 char **parts = g_strsplit(act, " ", 2);
2378 if (uzbl.state.verbose)
2379 printf ("Binding %-10s : %s\n", key, act);
2380 action = new_action(parts[0], parts[1]);
2382 if (g_hash_table_remove (uzbl.bindings, key))
2383 g_warning ("Overwriting existing binding for \"%s\"", key);
2384 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2389 get_xdg_var (XDG_Var xdg) {
2390 const gchar* actual_value = getenv (xdg.environmental);
2391 const gchar* home = getenv ("HOME");
2392 gchar* return_value;
2394 if (! actual_value || strcmp (actual_value, "") == 0) {
2395 if (xdg.default_value) {
2396 return_value = str_replace ("~", home, xdg.default_value);
2398 return_value = NULL;
2401 return_value = str_replace("~", home, actual_value);
2404 return return_value;
2408 find_xdg_file (int xdg_type, char* filename) {
2409 /* xdg_type = 0 => config
2410 xdg_type = 1 => data
2411 xdg_type = 2 => cache*/
2413 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2414 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2417 gchar* temporary_string;
2421 if (! file_exists (temporary_file) && xdg_type != 2) {
2422 buf = get_xdg_var (XDG[3 + xdg_type]);
2423 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2426 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2427 g_free (temporary_file);
2428 temporary_file = g_strconcat (temporary_string, filename, NULL);
2432 //g_free (temporary_string); - segfaults.
2434 if (file_exists (temporary_file)) {
2435 return temporary_file;
2442 State *s = &uzbl.state;
2443 Network *n = &uzbl.net;
2445 for (i = 0; default_config[i].command != NULL; i++) {
2446 parse_cmd_line(default_config[i].command, NULL);
2449 if (g_strcmp0(s->config_file, "-") == 0) {
2450 s->config_file = NULL;
2454 else if (!s->config_file) {
2455 s->config_file = find_xdg_file (0, "/uzbl/config");
2458 if (s->config_file) {
2459 GArray* lines = read_file_by_line (s->config_file);
2463 while ((line = g_array_index(lines, gchar*, i))) {
2464 parse_cmd_line (line, NULL);
2468 g_array_free (lines, TRUE);
2470 if (uzbl.state.verbose)
2471 printf ("No configuration file loaded.\n");
2474 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2477 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2480 if (!uzbl.behave.cookie_handler)
2483 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2484 GString *s = g_string_new ("");
2485 SoupURI * soup_uri = soup_message_get_uri(msg);
2486 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2487 run_handler(uzbl.behave.cookie_handler, s->str);
2489 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2490 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2491 if ( p != NULL ) *p = '\0';
2492 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2494 if (uzbl.comm.sync_stdout)
2495 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2497 g_string_free(s, TRUE);
2501 save_cookies (SoupMessage *msg, gpointer user_data){
2505 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2506 cookie = soup_cookie_to_set_cookie_header(ck->data);
2507 SoupURI * soup_uri = soup_message_get_uri(msg);
2508 GString *s = g_string_new ("");
2509 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2510 run_handler(uzbl.behave.cookie_handler, s->str);
2512 g_string_free(s, TRUE);
2517 /* --- WEBINSPECTOR --- */
2519 hide_window_cb(GtkWidget *widget, gpointer data) {
2522 gtk_widget_hide(widget);
2525 static WebKitWebView*
2526 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2529 (void) web_inspector;
2530 GtkWidget* scrolled_window;
2531 GtkWidget* new_web_view;
2534 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2535 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2536 G_CALLBACK(hide_window_cb), NULL);
2538 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2539 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2540 gtk_widget_show(g->inspector_window);
2542 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2543 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2544 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2545 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2546 gtk_widget_show(scrolled_window);
2548 new_web_view = webkit_web_view_new();
2549 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2551 return WEBKIT_WEB_VIEW(new_web_view);
2555 inspector_show_window_cb (WebKitWebInspector* inspector){
2557 gtk_widget_show(uzbl.gui.inspector_window);
2561 /* TODO: Add variables and code to make use of these functions */
2563 inspector_close_window_cb (WebKitWebInspector* inspector){
2569 inspector_attach_window_cb (WebKitWebInspector* inspector){
2575 inspector_detach_window_cb (WebKitWebInspector* inspector){
2581 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2587 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2593 set_up_inspector() {
2595 WebKitWebSettings *settings = view_settings();
2596 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2598 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2599 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2600 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2601 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2602 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2603 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2604 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2606 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2610 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2612 uzbl_cmdprop *c = v;
2617 if(c->type == TYPE_STR)
2618 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2619 else if(c->type == TYPE_INT)
2620 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2624 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2628 printf("bind %s = %s %s\n", (char *)k ,
2629 (char *)a->name, a->param?(char *)a->param:"");
2634 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2635 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2638 /* set up gtk, gobject, variable defaults and other things that tests and other
2639 * external applications need to do anyhow */
2641 initialize(int argc, char *argv[]) {
2642 gtk_init (&argc, &argv);
2643 if (!g_thread_supported ())
2644 g_thread_init (NULL);
2645 uzbl.state.executable_path = g_strdup(argv[0]);
2646 uzbl.state.selected_url = NULL;
2647 uzbl.state.searchtx = NULL;
2649 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2650 g_option_context_add_main_entries (context, entries, NULL);
2651 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2652 g_option_context_parse (context, &argc, &argv, NULL);
2653 g_option_context_free(context);
2655 /* initialize hash table */
2656 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2658 uzbl.net.soup_session = webkit_get_default_session();
2659 uzbl.state.keycmd = g_string_new("");
2661 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2662 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2663 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2664 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2665 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2666 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2668 uzbl.gui.sbar.progress_s = g_strdup("=");
2669 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2670 uzbl.gui.sbar.progress_w = 10;
2672 /* HTML mode defaults*/
2673 uzbl.behave.html_buffer = g_string_new("");
2674 uzbl.behave.html_endmarker = g_strdup(".");
2675 uzbl.behave.html_timeout = 60;
2676 uzbl.behave.base_url = g_strdup("http://invalid");
2678 /* default mode indicators */
2679 uzbl.behave.insert_indicator = g_strdup("I");
2680 uzbl.behave.cmd_indicator = g_strdup("C");
2682 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2683 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2684 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2685 uzbl.info.arch = ARCH;
2686 uzbl.info.commit = COMMIT;
2690 make_var_to_name_hash();
2692 uzbl.gui.scrolled_win = create_browser();
2695 #ifndef UZBL_LIBRARY
2698 main (int argc, char* argv[]) {
2699 initialize(argc, argv);
2701 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2705 /* initial packing */
2706 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2707 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2709 if (uzbl.state.socket_id) {
2710 uzbl.gui.plug = create_plug ();
2711 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2712 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2714 uzbl.gui.main_window = create_window ();
2715 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2716 gtk_widget_show_all (uzbl.gui.main_window);
2717 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2720 if(!uzbl.state.instance_name)
2721 uzbl.state.instance_name = itos((int)uzbl.xwin);
2723 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2725 if (uzbl.state.verbose) {
2726 printf("Uzbl start location: %s\n", argv[0]);
2727 if (uzbl.state.socket_id)
2728 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2730 printf("window_id %i\n",(int) uzbl.xwin);
2731 printf("pid %i\n", getpid ());
2732 printf("name: %s\n", uzbl.state.instance_name);
2735 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2736 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2737 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2738 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2739 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2743 if (!uzbl.behave.show_status)
2744 gtk_widget_hide(uzbl.gui.mainbar);
2751 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2752 gboolean verbose_override = uzbl.state.verbose;
2754 if (verbose_override > uzbl.state.verbose)
2755 uzbl.state.verbose = verbose_override;
2758 set_var_value("uri", uri_override);
2759 g_free(uri_override);
2760 } else if (uzbl.state.uri)
2761 cmd_load_uri(uzbl.gui.web_view, NULL);
2766 return EXIT_SUCCESS;
2770 /* vi: set et ts=4: */