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 { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
79 "Print the version and exit", NULL },
80 { NULL, 0, 0, 0, NULL, NULL, NULL }
83 /* associate command names to their properties */
84 typedef const struct {
92 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
94 /* abbreviations to help keep the table's width humane */
95 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
96 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
101 } var_name_to_ptr[] = {
102 /* variable name pointer to variable in code type dump callback function */
103 /* ---------------------------------------------------------------------------------------------- */
104 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
105 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
106 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
107 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
108 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
109 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
110 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
111 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
112 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
113 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
114 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
115 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
116 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
117 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
118 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
119 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_title)},
120 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_title)},
121 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
122 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
123 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
124 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, NULL)},
125 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
126 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
127 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
128 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
129 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
130 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
131 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
132 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
133 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
134 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
135 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
136 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
137 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
138 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
139 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
140 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
141 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
142 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
143 /* exported WebKitWebSettings properties */
144 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
145 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
146 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
147 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
148 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
149 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
150 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
151 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
152 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
153 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
154 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
155 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
156 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
157 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
158 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
159 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
161 /* constants (not dumpable or writeable) */
162 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
163 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
164 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
165 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
166 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
167 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
168 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
169 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
170 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
171 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
172 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
174 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
175 }, *n2v_p = var_name_to_ptr;
182 { "SHIFT", GDK_SHIFT_MASK }, // shift
183 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
184 { "CONTROL", GDK_CONTROL_MASK }, // control
185 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
186 { "MOD2", GDK_MOD2_MASK }, // 5th mod
187 { "MOD3", GDK_MOD3_MASK }, // 6th mod
188 { "MOD4", GDK_MOD4_MASK }, // 7th mod
189 { "MOD5", GDK_MOD5_MASK }, // 8th mod
190 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
191 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
192 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
193 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
194 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
195 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
196 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
197 { "META", GDK_META_MASK }, // meta (since 2.10)
202 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
203 * for quick access */
205 make_var_to_name_hash() {
206 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
208 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
213 /* --- UTILITY FUNCTIONS --- */
215 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
217 get_exp_type(gchar *s) {
221 else if(*(s+1) == '{')
222 return EXP_BRACED_VAR;
223 else if(*(s+1) == '<')
226 return EXP_SIMPLE_VAR;
232 * recurse == 1: don't expand '@(command)@'
233 * recurse == 2: don't expand '@<java script>@'
236 expand(char *s, guint recurse, gboolean escape_markup) {
240 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
245 gchar *cmd_stdout = NULL;
247 GString *buf = g_string_new("");
248 GString *js_ret = g_string_new("");
253 g_string_append_c(buf, *++s);
258 etype = get_exp_type(s);
263 if( (vend = strpbrk(s, end_simple_var)) ||
264 (vend = strchr(s, '\0')) ) {
265 strncpy(ret, s, vend-s);
271 if( (vend = strchr(s, upto)) ||
272 (vend = strchr(s, '\0')) ) {
273 strncpy(ret, s, vend-s);
279 strcpy(str_end, ")@");
281 if( (vend = strstr(s, str_end)) ||
282 (vend = strchr(s, '\0')) ) {
283 strncpy(ret, s, vend-s);
289 strcpy(str_end, ">@");
291 if( (vend = strstr(s, str_end)) ||
292 (vend = strchr(s, '\0')) ) {
293 strncpy(ret, s, vend-s);
299 if(etype == EXP_SIMPLE_VAR ||
300 etype == EXP_BRACED_VAR) {
301 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
302 if(c->type == TYPE_STR && *c->ptr != NULL) {
304 char *b = g_markup_escape_text((gchar *)*c->ptr,
305 strlen((gchar *)*c->ptr));
306 g_string_append(buf, b);
309 g_string_append(buf, (gchar *)*c->ptr);
311 } else if(c->type == TYPE_INT) {
312 char *b = itos((uintptr_t)*c->ptr);
313 g_string_append(buf, b);
318 if(etype == EXP_SIMPLE_VAR)
323 else if(recurse != 1 &&
325 mycmd = expand(ret, 1, escape_markup);
326 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
330 g_printerr("error on running command: %s\n", err->message);
333 else if (*cmd_stdout) {
334 int len = strlen(cmd_stdout);
336 if(cmd_stdout[len-1] == '\n')
337 cmd_stdout[--len] = 0; /* strip trailing newline */
340 char *b = g_markup_escape_text(cmd_stdout, len);
341 g_string_append(buf, b);
344 g_string_append(buf, cmd_stdout);
350 else if(recurse != 2 &&
352 mycmd = expand(ret, 2, escape_markup);
353 eval_js(uzbl.gui.web_view, mycmd, js_ret);
358 char *b = g_markup_escape_text(js_ret->str,
359 strlen(js_ret->str));
360 g_string_append(buf, b);
363 g_string_append(buf, js_ret->str);
365 g_string_free(js_ret, TRUE);
366 js_ret = g_string_new("");
373 g_string_append_c(buf, *s);
378 g_string_free(js_ret, TRUE);
379 return g_string_free(buf, FALSE);
386 snprintf(tmp, sizeof(tmp), "%i", val);
387 return g_strdup(tmp);
391 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
394 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
397 str_replace (const char* search, const char* replace, const char* string) {
401 buf = g_strsplit (string, search, -1);
402 ret = g_strjoinv (replace, buf);
403 g_strfreev(buf); // somebody said this segfaults
409 read_file_by_line (gchar *path) {
410 GIOChannel *chan = NULL;
411 gchar *readbuf = NULL;
413 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
416 chan = g_io_channel_new_file(path, "r", NULL);
419 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
420 const gchar* val = g_strdup (readbuf);
421 g_array_append_val (lines, val);
426 g_io_channel_unref (chan);
428 fprintf(stderr, "File '%s' not be read.\n", path);
435 gchar* parseenv (char* string) {
436 extern char** environ;
437 gchar* tmpstr = NULL;
441 while (environ[i] != NULL) {
442 gchar** env = g_strsplit (environ[i], "=", 2);
443 gchar* envname = g_strconcat ("$", env[0], NULL);
445 if (g_strrstr (string, envname) != NULL) {
446 tmpstr = g_strdup(string);
448 string = str_replace(envname, env[1], tmpstr);
453 g_strfreev (env); // somebody said this breaks uzbl
461 setup_signal(int signr, sigfunc *shandler) {
462 struct sigaction nh, oh;
464 nh.sa_handler = shandler;
465 sigemptyset(&nh.sa_mask);
468 if(sigaction(signr, &nh, &oh) < 0)
476 if (uzbl.behave.fifo_dir)
477 unlink (uzbl.comm.fifo_path);
478 if (uzbl.behave.socket_dir)
479 unlink (uzbl.comm.socket_path);
481 g_free(uzbl.state.executable_path);
482 g_string_free(uzbl.state.keycmd, TRUE);
483 g_hash_table_destroy(uzbl.bindings);
484 g_hash_table_destroy(uzbl.behave.commands);
487 /* used for html_mode_timeout
488 * be sure to extend this function to use
489 * more timers if needed in other places
492 set_timeout(int seconds) {
494 memset(&t, 0, sizeof t);
496 t.it_value.tv_sec = seconds;
497 t.it_value.tv_usec = 0;
498 setitimer(ITIMER_REAL, &t, NULL);
501 /* --- SIGNAL HANDLER --- */
504 catch_sigterm(int s) {
510 catch_sigint(int s) {
520 set_var_value("mode", "0");
525 /* --- CALLBACKS --- */
528 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
531 (void) navigation_action;
532 (void) policy_decision;
534 const gchar* uri = webkit_network_request_get_uri (request);
535 if (uzbl.state.verbose)
536 printf("New window requested -> %s \n", uri);
537 webkit_web_policy_decision_use(policy_decision);
542 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
547 /* If we can display it, let's display it... */
548 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
549 webkit_web_policy_decision_use (policy_decision);
553 /* ...everything we can't displayed is downloaded */
554 webkit_web_policy_decision_download (policy_decision);
559 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
563 if (uzbl.state.selected_url != NULL) {
564 if (uzbl.state.verbose)
565 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
566 new_window_load_uri(uzbl.state.selected_url);
568 if (uzbl.state.verbose)
569 printf("New web view -> %s\n","Nothing to open, exiting");
575 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
578 if (uzbl.behave.download_handler) {
579 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
580 if (uzbl.state.verbose)
581 printf("Download -> %s\n",uri);
582 /* if urls not escaped, we may have to escape and quote uri before this call */
583 run_handler(uzbl.behave.download_handler, uri);
588 /* scroll a bar in a given direction */
590 scroll (GtkAdjustment* bar, GArray *argv) {
594 gdouble page_size = gtk_adjustment_get_page_size(bar);
595 gdouble value = gtk_adjustment_get_value(bar);
596 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
599 value += page_size * amount * 0.01;
603 max_value = gtk_adjustment_get_upper(bar) - page_size;
605 if (value > max_value)
606 value = max_value; /* don't scroll past the end of the page */
608 gtk_adjustment_set_value (bar, value);
612 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
613 (void) page; (void) argv; (void) result;
614 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
618 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
619 (void) page; (void) argv; (void) result;
620 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
621 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
625 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
626 (void) page; (void) result;
627 scroll(uzbl.gui.bar_v, argv);
631 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
632 (void) page; (void) result;
633 scroll(uzbl.gui.bar_h, argv);
638 if (!uzbl.behave.show_status) {
639 gtk_widget_hide(uzbl.gui.mainbar);
641 gtk_widget_show(uzbl.gui.mainbar);
647 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
652 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
656 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
661 if (uzbl.behave.show_status) {
662 gtk_widget_hide(uzbl.gui.mainbar);
664 gtk_widget_show(uzbl.gui.mainbar);
666 uzbl.behave.show_status = !uzbl.behave.show_status;
671 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
675 //Set selected_url state variable
676 g_free(uzbl.state.selected_url);
677 uzbl.state.selected_url = NULL;
679 uzbl.state.selected_url = g_strdup(link);
685 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
688 const gchar *title = webkit_web_view_get_title(web_view);
689 if (uzbl.gui.main_title)
690 g_free (uzbl.gui.main_title);
691 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
696 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
699 uzbl.gui.sbar.load_progress = progress;
701 g_free(uzbl.gui.sbar.progress_bar);
702 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
708 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
712 if (uzbl.behave.load_finish_handler)
713 run_handler(uzbl.behave.load_finish_handler, "");
717 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
721 uzbl.gui.sbar.load_progress = 0;
722 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
723 if (uzbl.behave.load_start_handler)
724 run_handler(uzbl.behave.load_start_handler, "");
728 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
731 g_free (uzbl.state.uri);
732 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
733 uzbl.state.uri = g_string_free (newuri, FALSE);
734 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
735 set_insert_mode(uzbl.behave.always_insert_mode);
738 if (uzbl.behave.load_commit_handler)
739 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
743 destroy_cb (GtkWidget* widget, gpointer data) {
751 if (uzbl.behave.history_handler) {
753 struct tm * timeinfo;
756 timeinfo = localtime ( &rawtime );
757 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
758 run_handler(uzbl.behave.history_handler, date);
763 /* VIEW funcs (little webkit wrappers) */
764 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
766 VIEWFUNC(reload_bypass_cache)
767 VIEWFUNC(stop_loading)
774 /* -- command to callback/function map for things we cannot attach to any signals */
775 static struct {char *key; CommandInfo value;} cmdlist[] =
776 { /* key function no_split */
777 { "back", {view_go_back, 0} },
778 { "forward", {view_go_forward, 0} },
779 { "scroll_vert", {scroll_vert, 0} },
780 { "scroll_horz", {scroll_horz, 0} },
781 { "scroll_begin", {scroll_begin, 0} },
782 { "scroll_end", {scroll_end, 0} },
783 { "reload", {view_reload, 0}, },
784 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
785 { "stop", {view_stop_loading, 0}, },
786 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
787 { "zoom_out", {view_zoom_out, 0}, },
788 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
789 { "uri", {load_uri, TRUE} },
790 { "js", {run_js, TRUE} },
791 { "script", {run_external_js, 0} },
792 { "toggle_status", {toggle_status_cb, 0} },
793 { "spawn", {spawn, 0} },
794 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
795 { "sh", {spawn_sh, 0} },
796 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
797 { "exit", {close_uzbl, 0} },
798 { "search", {search_forward_text, TRUE} },
799 { "search_reverse", {search_reverse_text, TRUE} },
800 { "dehilight", {dehilight, 0} },
801 { "toggle_insert_mode", {toggle_insert_mode, 0} },
802 { "set", {set_var, TRUE} },
803 //{ "get", {get_var, TRUE} },
804 { "bind", {act_bind, TRUE} },
805 { "dump_config", {act_dump_config, 0} },
806 { "keycmd", {keycmd, TRUE} },
807 { "keycmd_nl", {keycmd_nl, TRUE} },
808 { "keycmd_bs", {keycmd_bs, 0} },
809 { "chain", {chain, 0} },
810 { "print", {print, TRUE} }
817 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
819 for (i = 0; i < LENGTH(cmdlist); i++)
820 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
823 /* -- CORE FUNCTIONS -- */
826 free_action(gpointer act) {
827 Action *action = (Action*)act;
828 g_free(action->name);
830 g_free(action->param);
835 new_action(const gchar *name, const gchar *param) {
836 Action *action = g_new(Action, 1);
838 action->name = g_strdup(name);
840 action->param = g_strdup(param);
842 action->param = NULL;
848 file_exists (const char * filename) {
849 return (access(filename, F_OK) == 0);
853 set_var(WebKitWebView *page, GArray *argv, GString *result) {
854 (void) page; (void) result;
855 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
856 if (split[0] != NULL) {
857 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
858 set_var_value(g_strstrip(split[0]), value);
865 print(WebKitWebView *page, GArray *argv, GString *result) {
866 (void) page; (void) result;
869 buf = expand(argv_idx(argv, 0), 0, FALSE);
870 g_string_assign(result, buf);
875 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
876 (void) page; (void) result;
877 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
878 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
879 add_binding(g_strstrip(split[0]), value);
890 void set_insert_mode(gboolean mode) {
891 uzbl.behave.insert_mode = mode;
892 uzbl.gui.sbar.mode_indicator = (mode ?
893 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
897 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
898 (void) page; (void) result;
900 if (argv_idx(argv, 0)) {
901 if (strcmp (argv_idx(argv, 0), "0") == 0) {
902 set_insert_mode(FALSE);
904 set_insert_mode(TRUE);
907 set_insert_mode( !uzbl.behave.insert_mode );
914 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
917 if (argv_idx(argv, 0)) {
918 GString* newuri = g_string_new (argv_idx(argv, 0));
919 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
920 run_js(web_view, argv, NULL);
923 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
924 g_string_prepend (newuri, "http://");
925 /* if we do handle cookies, ask our handler for them */
926 webkit_web_view_load_uri (web_view, newuri->str);
927 g_string_free (newuri, TRUE);
935 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
936 size_t argumentCount, const JSValueRef arguments[],
937 JSValueRef* exception) {
942 JSStringRef js_result_string;
943 GString *result = g_string_new("");
945 if (argumentCount >= 1) {
946 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
947 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
948 char ctl_line[arg_size];
949 JSStringGetUTF8CString(arg, ctl_line, arg_size);
951 parse_cmd_line(ctl_line, result);
953 JSStringRelease(arg);
955 js_result_string = JSStringCreateWithUTF8CString(result->str);
957 g_string_free(result, TRUE);
959 return JSValueMakeString(ctx, js_result_string);
962 static JSStaticFunction js_static_functions[] = {
963 {"run", js_run_command, kJSPropertyAttributeNone},
968 /* This function creates the class and its definition, only once */
969 if (!uzbl.js.initialized) {
970 /* it would be pretty cool to make this dynamic */
971 uzbl.js.classdef = kJSClassDefinitionEmpty;
972 uzbl.js.classdef.staticFunctions = js_static_functions;
974 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
980 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
981 WebKitWebFrame *frame;
982 JSGlobalContextRef context;
983 JSObjectRef globalobject;
984 JSStringRef var_name;
986 JSStringRef js_script;
987 JSValueRef js_result;
988 JSStringRef js_result_string;
989 size_t js_result_size;
993 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
994 context = webkit_web_frame_get_global_context(frame);
995 globalobject = JSContextGetGlobalObject(context);
997 /* uzbl javascript namespace */
998 var_name = JSStringCreateWithUTF8CString("Uzbl");
999 JSObjectSetProperty(context, globalobject, var_name,
1000 JSObjectMake(context, uzbl.js.classref, NULL),
1001 kJSClassAttributeNone, NULL);
1003 /* evaluate the script and get return value*/
1004 js_script = JSStringCreateWithUTF8CString(script);
1005 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1006 if (js_result && !JSValueIsUndefined(context, js_result)) {
1007 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1008 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1010 if (js_result_size) {
1011 char js_result_utf8[js_result_size];
1012 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1013 g_string_assign(result, js_result_utf8);
1016 JSStringRelease(js_result_string);
1020 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1022 JSStringRelease(var_name);
1023 JSStringRelease(js_script);
1027 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1029 if (argv_idx(argv, 0))
1030 eval_js(web_view, argv_idx(argv, 0), result);
1034 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1036 if (argv_idx(argv, 0)) {
1037 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1042 while ((line = g_array_index(lines, gchar*, i))) {
1044 js = g_strdup (line);
1046 gchar* newjs = g_strconcat (js, line, NULL);
1053 if (uzbl.state.verbose)
1054 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1056 if (argv_idx (argv, 1)) {
1057 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1061 eval_js (web_view, js, result);
1063 g_array_free (lines, TRUE);
1068 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1069 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1070 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1071 webkit_web_view_unmark_text_matches (page);
1072 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1073 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1077 if (uzbl.state.searchtx) {
1078 if (uzbl.state.verbose)
1079 printf ("Searching: %s\n", uzbl.state.searchtx);
1080 webkit_web_view_set_highlight_text_matches (page, TRUE);
1081 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1086 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1088 search_text(page, argv, TRUE);
1092 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1094 search_text(page, argv, FALSE);
1098 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1099 (void) argv; (void) result;
1100 webkit_web_view_set_highlight_text_matches (page, FALSE);
1105 new_window_load_uri (const gchar * uri) {
1106 if (uzbl.behave.new_window) {
1107 GString *s = g_string_new ("");
1108 g_string_printf(s, "'%s'", uri);
1109 run_handler(uzbl.behave.new_window, s->str);
1112 GString* to_execute = g_string_new ("");
1113 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1115 for (i = 0; entries[i].long_name != NULL; i++) {
1116 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1117 gchar** str = (gchar**)entries[i].arg_data;
1119 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1123 if (uzbl.state.verbose)
1124 printf("\n%s\n", to_execute->str);
1125 g_spawn_command_line_async (to_execute->str, NULL);
1126 g_string_free (to_execute, TRUE);
1130 chain (WebKitWebView *page, GArray *argv, GString *result) {
1131 (void) page; (void) result;
1133 gchar **parts = NULL;
1135 while ((a = argv_idx(argv, i++))) {
1136 parts = g_strsplit (a, " ", 2);
1137 parse_command(parts[0], parts[1], result);
1143 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1147 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1153 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1157 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1163 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1168 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1170 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1175 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1182 /* --Statusbar functions-- */
1184 build_progressbar_ascii(int percent) {
1185 int width=uzbl.gui.sbar.progress_w;
1188 GString *bar = g_string_new("");
1190 l = (double)percent*((double)width/100.);
1191 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1193 for(i=0; i<(int)l; i++)
1194 g_string_append(bar, uzbl.gui.sbar.progress_s);
1197 g_string_append(bar, uzbl.gui.sbar.progress_u);
1199 return g_string_free(bar, FALSE);
1204 const GScannerConfig scan_config = {
1207 ) /* cset_skip_characters */,
1212 ) /* cset_identifier_first */,
1219 ) /* cset_identifier_nth */,
1220 ( "" ) /* cpair_comment_single */,
1222 TRUE /* case_sensitive */,
1224 FALSE /* skip_comment_multi */,
1225 FALSE /* skip_comment_single */,
1226 FALSE /* scan_comment_multi */,
1227 TRUE /* scan_identifier */,
1228 TRUE /* scan_identifier_1char */,
1229 FALSE /* scan_identifier_NULL */,
1230 TRUE /* scan_symbols */,
1231 FALSE /* scan_binary */,
1232 FALSE /* scan_octal */,
1233 FALSE /* scan_float */,
1234 FALSE /* scan_hex */,
1235 FALSE /* scan_hex_dollar */,
1236 FALSE /* scan_string_sq */,
1237 FALSE /* scan_string_dq */,
1238 TRUE /* numbers_2_int */,
1239 FALSE /* int_2_float */,
1240 FALSE /* identifier_2_string */,
1241 FALSE /* char_2_token */,
1242 FALSE /* symbol_2_token */,
1243 TRUE /* scope_0_fallback */,
1248 uzbl.scan = g_scanner_new(&scan_config);
1249 while(symp->symbol_name) {
1250 g_scanner_scope_add_symbol(uzbl.scan, 0,
1252 GINT_TO_POINTER(symp->symbol_token));
1258 expand_template(const char *template, gboolean escape_markup) {
1259 if(!template) return NULL;
1261 GTokenType token = G_TOKEN_NONE;
1262 GString *ret = g_string_new("");
1266 g_scanner_input_text(uzbl.scan, template, strlen(template));
1267 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1268 token = g_scanner_get_next_token(uzbl.scan);
1270 if(token == G_TOKEN_SYMBOL) {
1271 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1275 buf = uzbl.state.keycmd->str?
1276 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1277 g_string_append(ret, buf);
1281 g_string_append(ret, uzbl.state.keycmd->str?
1282 uzbl.state.keycmd->str:g_strdup(""));
1288 else if(token == G_TOKEN_INT) {
1289 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1290 g_string_append(ret, buf);
1293 else if(token == G_TOKEN_IDENTIFIER) {
1294 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1296 else if(token == G_TOKEN_CHAR) {
1297 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1301 return g_string_free(ret, FALSE);
1303 /* --End Statusbar functions-- */
1306 sharg_append(GArray *a, const gchar *str) {
1307 const gchar *s = (str ? str : "");
1308 g_array_append_val(a, s);
1311 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1313 run_command (const gchar *command, const guint npre, const gchar **args,
1314 const gboolean sync, char **output_stdout) {
1315 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1318 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1319 gchar *pid = itos(getpid());
1320 gchar *xwin = itos(uzbl.xwin);
1322 sharg_append(a, command);
1323 for (i = 0; i < npre; i++) /* add n args before the default vars */
1324 sharg_append(a, args[i]);
1325 sharg_append(a, uzbl.state.config_file);
1326 sharg_append(a, pid);
1327 sharg_append(a, xwin);
1328 sharg_append(a, uzbl.comm.fifo_path);
1329 sharg_append(a, uzbl.comm.socket_path);
1330 sharg_append(a, uzbl.state.uri);
1331 sharg_append(a, uzbl.gui.main_title);
1333 for (i = npre; i < g_strv_length((gchar**)args); i++)
1334 sharg_append(a, args[i]);
1338 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1340 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1341 NULL, NULL, output_stdout, NULL, NULL, &err);
1342 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1343 NULL, NULL, NULL, &err);
1345 if (uzbl.state.verbose) {
1346 GString *s = g_string_new("spawned:");
1347 for (i = 0; i < (a->len); i++) {
1348 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1349 g_string_append_printf(s, " %s", qarg);
1352 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1353 printf("%s\n", s->str);
1354 g_string_free(s, TRUE);
1356 printf("Stdout: %s\n", *output_stdout);
1360 g_printerr("error on run_command: %s\n", err->message);
1365 g_array_free (a, TRUE);
1370 split_quoted(const gchar* src, const gboolean unquote) {
1371 /* split on unquoted space, return array of strings;
1372 remove a layer of quotes and backslashes if unquote */
1373 if (!src) return NULL;
1375 gboolean dq = FALSE;
1376 gboolean sq = FALSE;
1377 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1378 GString *s = g_string_new ("");
1382 for (p = src; *p != '\0'; p++) {
1383 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1384 else if (*p == '\\') { g_string_append_c(s, *p++);
1385 g_string_append_c(s, *p); }
1386 else if ((*p == '"') && unquote && !sq) dq = !dq;
1387 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1389 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1390 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1392 else if ((*p == ' ') && !dq && !sq) {
1393 dup = g_strdup(s->str);
1394 g_array_append_val(a, dup);
1395 g_string_truncate(s, 0);
1396 } else g_string_append_c(s, *p);
1398 dup = g_strdup(s->str);
1399 g_array_append_val(a, dup);
1400 ret = (gchar**)a->data;
1401 g_array_free (a, FALSE);
1402 g_string_free (s, TRUE);
1407 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1408 (void)web_view; (void)result;
1409 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1410 if (argv_idx(argv, 0))
1411 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1415 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1416 (void)web_view; (void)result;
1418 if (argv_idx(argv, 0))
1419 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1420 TRUE, &uzbl.comm.sync_stdout);
1424 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1425 (void)web_view; (void)result;
1426 if (!uzbl.behave.shell_cmd) {
1427 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1432 gchar *spacer = g_strdup("");
1433 g_array_insert_val(argv, 1, spacer);
1434 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1436 for (i = 1; i < g_strv_length(cmd); i++)
1437 g_array_prepend_val(argv, cmd[i]);
1439 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1445 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1446 (void)web_view; (void)result;
1447 if (!uzbl.behave.shell_cmd) {
1448 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1453 gchar *spacer = g_strdup("");
1454 g_array_insert_val(argv, 1, spacer);
1455 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1457 for (i = 1; i < g_strv_length(cmd); i++)
1458 g_array_prepend_val(argv, cmd[i]);
1460 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1461 TRUE, &uzbl.comm.sync_stdout);
1467 parse_command(const char *cmd, const char *param, GString *result) {
1470 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1472 gchar **par = split_quoted(param, TRUE);
1473 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1475 if (c->no_split) { /* don't split */
1476 sharg_append(a, param);
1478 for (i = 0; i < g_strv_length(par); i++)
1479 sharg_append(a, par[i]);
1482 if (result == NULL) {
1483 GString *result_print = g_string_new("");
1485 c->function(uzbl.gui.web_view, a, result_print);
1486 if (result_print->len)
1487 printf("%*s\n", result_print->len, result_print->str);
1489 g_string_free(result_print, TRUE);
1491 c->function(uzbl.gui.web_view, a, result);
1494 g_array_free (a, TRUE);
1497 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1504 if(*uzbl.net.proxy_url == ' '
1505 || uzbl.net.proxy_url == NULL) {
1506 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1507 (GType) SOUP_SESSION_PROXY_URI);
1510 suri = soup_uri_new(uzbl.net.proxy_url);
1511 g_object_set(G_OBJECT(uzbl.net.soup_session),
1512 SOUP_SESSION_PROXY_URI,
1514 soup_uri_free(suri);
1521 if(file_exists(uzbl.gui.icon)) {
1522 if (uzbl.gui.main_window)
1523 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1525 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1531 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1532 g_array_append_val (a, uzbl.state.uri);
1533 load_uri(uzbl.gui.web_view, a, NULL);
1534 g_array_free (a, TRUE);
1538 cmd_always_insert_mode() {
1539 set_insert_mode(uzbl.behave.always_insert_mode);
1545 g_object_set(G_OBJECT(uzbl.net.soup_session),
1546 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1550 cmd_max_conns_host() {
1551 g_object_set(G_OBJECT(uzbl.net.soup_session),
1552 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1557 soup_session_remove_feature
1558 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1559 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1560 /*g_free(uzbl.net.soup_logger);*/
1562 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1563 soup_session_add_feature(uzbl.net.soup_session,
1564 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1567 static WebKitWebSettings*
1569 return webkit_web_view_get_settings(uzbl.gui.web_view);
1574 WebKitWebSettings *ws = view_settings();
1575 if (uzbl.behave.font_size > 0) {
1576 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1579 if (uzbl.behave.monospace_size > 0) {
1580 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1581 uzbl.behave.monospace_size, NULL);
1583 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1584 uzbl.behave.font_size, NULL);
1590 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1594 cmd_disable_plugins() {
1595 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1596 !uzbl.behave.disable_plugins, NULL);
1600 cmd_disable_scripts() {
1601 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1602 !uzbl.behave.disable_scripts, NULL);
1606 cmd_minimum_font_size() {
1607 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1608 uzbl.behave.minimum_font_size, NULL);
1611 cmd_autoload_img() {
1612 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1613 uzbl.behave.autoload_img, NULL);
1618 cmd_autoshrink_img() {
1619 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1620 uzbl.behave.autoshrink_img, NULL);
1625 cmd_enable_spellcheck() {
1626 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1627 uzbl.behave.enable_spellcheck, NULL);
1631 cmd_enable_private() {
1632 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1633 uzbl.behave.enable_private, NULL);
1638 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1639 uzbl.behave.print_bg, NULL);
1644 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1645 uzbl.behave.style_uri, NULL);
1649 cmd_resizable_txt() {
1650 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1651 uzbl.behave.resizable_txt, NULL);
1655 cmd_default_encoding() {
1656 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1657 uzbl.behave.default_encoding, NULL);
1661 cmd_enforce_96dpi() {
1662 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1663 uzbl.behave.enforce_96dpi, NULL);
1667 cmd_caret_browsing() {
1668 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1669 uzbl.behave.caret_browsing, NULL);
1673 cmd_cookie_handler() {
1674 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1675 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1676 if ((g_strcmp0(split[0], "sh") == 0) ||
1677 (g_strcmp0(split[0], "spawn") == 0)) {
1678 g_free (uzbl.behave.cookie_handler);
1679 uzbl.behave.cookie_handler =
1680 g_strdup_printf("sync_%s %s", split[0], split[1]);
1687 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1688 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1689 if ((g_strcmp0(split[0], "sh") == 0) ||
1690 (g_strcmp0(split[0], "spawn") == 0)) {
1691 g_free (uzbl.behave.new_window);
1692 uzbl.behave.new_window =
1693 g_strdup_printf("%s %s", split[0], split[1]);
1700 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1705 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1710 if(uzbl.behave.inject_html) {
1711 webkit_web_view_load_html_string (uzbl.gui.web_view,
1712 uzbl.behave.inject_html, NULL);
1721 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1722 uzbl.behave.modmask = 0;
1724 if(uzbl.behave.modkey)
1725 g_free(uzbl.behave.modkey);
1726 uzbl.behave.modkey = buf;
1728 for (i = 0; modkeys[i].key != NULL; i++) {
1729 if (g_strrstr(buf, modkeys[i].key))
1730 uzbl.behave.modmask |= modkeys[i].mask;
1736 if (*uzbl.net.useragent == ' ') {
1737 g_free (uzbl.net.useragent);
1738 uzbl.net.useragent = NULL;
1740 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1741 uzbl.net.useragent, NULL);
1747 gtk_widget_ref(uzbl.gui.scrolled_win);
1748 gtk_widget_ref(uzbl.gui.mainbar);
1749 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1750 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1752 if(uzbl.behave.status_top) {
1753 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1754 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1757 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1758 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1760 gtk_widget_unref(uzbl.gui.scrolled_win);
1761 gtk_widget_unref(uzbl.gui.mainbar);
1762 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1767 set_var_value(gchar *name, gchar *val) {
1768 uzbl_cmdprop *c = NULL;
1772 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1773 if(!c->writeable) return TRUE;
1775 /* check for the variable type */
1776 if (c->type == TYPE_STR) {
1777 buf = expand(val, 0, FALSE);
1780 } else if(c->type == TYPE_INT) {
1781 int *ip = (int *)c->ptr;
1782 buf = expand(val, 0, FALSE);
1783 *ip = (int)strtoul(buf, &endp, 10);
1785 } else if (c->type == TYPE_FLOAT) {
1786 float *fp = (float *)c->ptr;
1787 buf = expand(val, 0, FALSE);
1788 *fp = strtod(buf, &endp);
1792 /* invoke a command specific function */
1793 if(c->func) c->func();
1800 Behaviour *b = &uzbl.behave;
1802 if(b->html_buffer->str) {
1803 webkit_web_view_load_html_string (uzbl.gui.web_view,
1804 b->html_buffer->str, b->base_url);
1805 g_string_free(b->html_buffer, TRUE);
1806 b->html_buffer = g_string_new("");
1810 enum {M_CMD, M_HTML};
1812 parse_cmd_line(const char *ctl_line, GString *result) {
1813 Behaviour *b = &uzbl.behave;
1816 if(b->mode == M_HTML) {
1817 len = strlen(b->html_endmarker);
1818 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1819 if(len == strlen(ctl_line)-1 &&
1820 !strncmp(b->html_endmarker, ctl_line, len)) {
1822 set_var_value("mode", "0");
1827 set_timeout(b->html_timeout);
1828 g_string_append(b->html_buffer, ctl_line);
1831 else if((ctl_line[0] == '#') /* Comments */
1832 || (ctl_line[0] == ' ')
1833 || (ctl_line[0] == '\n'))
1834 ; /* ignore these lines */
1835 else { /* parse a command */
1837 gchar **tokens = NULL;
1838 len = strlen(ctl_line);
1840 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1841 ctlstrip = g_strndup(ctl_line, len - 1);
1842 else ctlstrip = g_strdup(ctl_line);
1844 tokens = g_strsplit(ctlstrip, " ", 2);
1845 parse_command(tokens[0], tokens[1], result);
1852 build_stream_name(int type, const gchar* dir) {
1853 State *s = &uzbl.state;
1857 str = g_strdup_printf
1858 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1859 } else if (type == SOCKET) {
1860 str = g_strdup_printf
1861 ("%s/uzbl_socket_%s", dir, s->instance_name);
1867 control_fifo(GIOChannel *gio, GIOCondition condition) {
1868 if (uzbl.state.verbose)
1869 printf("triggered\n");
1874 if (condition & G_IO_HUP)
1875 g_error ("Fifo: Read end of pipe died!\n");
1878 g_error ("Fifo: GIOChannel broke\n");
1880 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1881 if (ret == G_IO_STATUS_ERROR) {
1882 g_error ("Fifo: Error reading: %s\n", err->message);
1886 parse_cmd_line(ctl_line, NULL);
1893 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1894 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1895 if (unlink(uzbl.comm.fifo_path) == -1)
1896 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1897 g_free(uzbl.comm.fifo_path);
1898 uzbl.comm.fifo_path = NULL;
1901 if (*dir == ' ') { /* space unsets the variable */
1906 GIOChannel *chan = NULL;
1907 GError *error = NULL;
1908 gchar *path = build_stream_name(FIFO, dir);
1910 if (!file_exists(path)) {
1911 if (mkfifo (path, 0666) == 0) {
1912 // 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.
1913 chan = g_io_channel_new_file(path, "r+", &error);
1915 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1916 if (uzbl.state.verbose)
1917 printf ("init_fifo: created successfully as %s\n", path);
1918 uzbl.comm.fifo_path = path;
1920 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1921 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1922 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1923 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1925 /* if we got this far, there was an error; cleanup */
1926 if (error) g_error_free (error);
1933 control_stdin(GIOChannel *gio, GIOCondition condition) {
1935 gchar *ctl_line = NULL;
1938 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1939 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1942 parse_cmd_line(ctl_line, NULL);
1950 GIOChannel *chan = NULL;
1951 GError *error = NULL;
1953 chan = g_io_channel_unix_new(fileno(stdin));
1955 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1956 g_error ("Stdin: could not add watch\n");
1958 if (uzbl.state.verbose)
1959 printf ("Stdin: watch added successfully\n");
1962 g_error ("Stdin: Error while opening: %s\n", error->message);
1964 if (error) g_error_free (error);
1968 control_socket(GIOChannel *chan) {
1969 struct sockaddr_un remote;
1970 unsigned int t = sizeof(remote);
1972 GIOChannel *clientchan;
1974 clientsock = accept (g_io_channel_unix_get_fd(chan),
1975 (struct sockaddr *) &remote, &t);
1977 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1978 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1979 (GIOFunc) control_client_socket, clientchan);
1986 control_client_socket(GIOChannel *clientchan) {
1988 GString *result = g_string_new("");
1989 GError *error = NULL;
1993 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1994 if (ret == G_IO_STATUS_ERROR) {
1995 g_warning ("Error reading: %s\n", error->message);
1996 g_io_channel_shutdown(clientchan, TRUE, &error);
1998 } else if (ret == G_IO_STATUS_EOF) {
1999 /* shutdown and remove channel watch from main loop */
2000 g_io_channel_shutdown(clientchan, TRUE, &error);
2005 parse_cmd_line (ctl_line, result);
2006 g_string_append_c(result, '\n');
2007 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2009 if (ret == G_IO_STATUS_ERROR) {
2010 g_warning ("Error writing: %s", error->message);
2012 g_io_channel_flush(clientchan, &error);
2015 if (error) g_error_free (error);
2016 g_string_free(result, TRUE);
2022 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2023 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2024 if (unlink(uzbl.comm.socket_path) == -1)
2025 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2026 g_free(uzbl.comm.socket_path);
2027 uzbl.comm.socket_path = NULL;
2035 GIOChannel *chan = NULL;
2037 struct sockaddr_un local;
2038 gchar *path = build_stream_name(SOCKET, dir);
2040 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2042 local.sun_family = AF_UNIX;
2043 strcpy (local.sun_path, path);
2044 unlink (local.sun_path);
2046 len = strlen (local.sun_path) + sizeof (local.sun_family);
2047 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2048 if (uzbl.state.verbose)
2049 printf ("init_socket: opened in %s\n", path);
2052 if( (chan = g_io_channel_unix_new(sock)) ) {
2053 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2054 uzbl.comm.socket_path = path;
2057 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2059 /* if we got this far, there was an error; cleanup */
2066 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2067 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2069 // this function may be called very early when the templates are not set (yet), hence the checks
2071 update_title (void) {
2072 Behaviour *b = &uzbl.behave;
2075 if (b->show_status) {
2076 if (b->title_format_short) {
2077 parsed = expand_template(b->title_format_short, FALSE);
2078 parsed = expand(parsed, 0, FALSE);
2079 if (uzbl.gui.main_window)
2080 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2083 if (b->status_format) {
2084 parsed = expand_template(b->status_format, TRUE);
2085 parsed = expand(parsed, 0, TRUE);
2086 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2089 if (b->status_background) {
2091 gdk_color_parse (b->status_background, &color);
2092 //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2093 if (uzbl.gui.main_window)
2094 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2095 else if (uzbl.gui.plug)
2096 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2099 if (b->title_format_long) {
2100 parsed = expand_template(b->title_format_long, FALSE);
2101 parsed = expand(parsed, 0, FALSE);
2102 if (uzbl.gui.main_window)
2103 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2110 key_press_cb (GtkWidget* window, GdkEventKey* event)
2112 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2116 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2117 || 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)
2120 /* turn off insert mode (if always_insert_mode is not used) */
2121 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2122 set_insert_mode(uzbl.behave.always_insert_mode);
2127 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2130 if (event->keyval == GDK_Escape) {
2131 g_string_truncate(uzbl.state.keycmd, 0);
2133 dehilight(uzbl.gui.web_view, NULL, NULL);
2137 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2138 if (event->keyval == GDK_Insert) {
2140 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2141 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2143 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2146 g_string_append (uzbl.state.keycmd, str);
2153 if (event->keyval == GDK_BackSpace)
2154 keycmd_bs(NULL, NULL, NULL);
2156 gboolean key_ret = FALSE;
2157 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2159 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2161 run_keycmd(key_ret);
2163 if (key_ret) return (!uzbl.behave.insert_mode);
2168 run_keycmd(const gboolean key_ret) {
2169 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2171 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2172 g_string_truncate(uzbl.state.keycmd, 0);
2173 parse_command(act->name, act->param, NULL);
2177 /* try if it's an incremental keycmd or one that takes args, and run it */
2178 GString* short_keys = g_string_new ("");
2179 GString* short_keys_inc = g_string_new ("");
2181 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2182 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2183 g_string_assign(short_keys_inc, short_keys->str);
2184 g_string_append_c(short_keys, '_');
2185 g_string_append_c(short_keys_inc, '*');
2187 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2188 /* run normal cmds only if return was pressed */
2189 exec_paramcmd(act, i);
2190 g_string_truncate(uzbl.state.keycmd, 0);
2192 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2193 if (key_ret) /* just quit the incremental command on return */
2194 g_string_truncate(uzbl.state.keycmd, 0);
2195 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2199 g_string_truncate(short_keys, short_keys->len - 1);
2201 g_string_free (short_keys, TRUE);
2202 g_string_free (short_keys_inc, TRUE);
2206 exec_paramcmd(const Action *act, const guint i) {
2207 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2208 GString *actionname = g_string_new ("");
2209 GString *actionparam = g_string_new ("");
2210 g_string_erase (parampart, 0, i+1);
2212 g_string_printf (actionname, act->name, parampart->str);
2214 g_string_printf (actionparam, act->param, parampart->str);
2215 parse_command(actionname->str, actionparam->str, NULL);
2216 g_string_free(actionname, TRUE);
2217 g_string_free(actionparam, TRUE);
2218 g_string_free(parampart, TRUE);
2226 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2227 //main_window_ref = g_object_ref(scrolled_window);
2228 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
2230 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2231 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2233 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2234 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2235 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2236 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2237 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2238 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2239 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2240 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2241 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2242 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2243 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2245 return scrolled_window;
2252 g->mainbar = gtk_hbox_new (FALSE, 0);
2254 /* keep a reference to the bar so we can re-pack it at runtime*/
2255 //sbar_ref = g_object_ref(g->mainbar);
2257 g->mainbar_label = gtk_label_new ("");
2258 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2259 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2260 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2261 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2262 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2263 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2268 GtkWidget* create_window () {
2269 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2270 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2271 gtk_widget_set_name (window, "Uzbl browser");
2272 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2273 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2279 GtkPlug* create_plug () {
2280 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2281 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2282 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2289 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2291 If actname is one that calls an external command, this function will inject
2292 newargs in front of the user-provided args in that command line. They will
2293 come become after the body of the script (in sh) or after the name of
2294 the command to execute (in spawn).
2295 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2296 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2298 The return value consist of two strings: the action (sh, ...) and its args.
2300 If act is not one that calls an external command, then the given action merely
2303 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2304 gchar *actdup = g_strdup(actname);
2305 g_array_append_val(rets, actdup);
2307 if ((g_strcmp0(actname, "spawn") == 0) ||
2308 (g_strcmp0(actname, "sh") == 0) ||
2309 (g_strcmp0(actname, "sync_spawn") == 0) ||
2310 (g_strcmp0(actname, "sync_sh") == 0)) {
2312 GString *a = g_string_new("");
2313 gchar **spawnparts = split_quoted(origargs, FALSE);
2314 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2315 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2317 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2318 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2320 g_array_append_val(rets, a->str);
2321 g_string_free(a, FALSE);
2322 g_strfreev(spawnparts);
2324 gchar *origdup = g_strdup(origargs);
2325 g_array_append_val(rets, origdup);
2327 return (gchar**)g_array_free(rets, FALSE);
2331 run_handler (const gchar *act, const gchar *args) {
2332 /* Consider this code a temporary hack to make the handlers usable.
2333 In practice, all this splicing, injection, and reconstruction is
2334 inefficient, annoying and hard to manage. Potential pitfalls arise
2335 when the handler specific args 1) are not quoted (the handler
2336 callbacks should take care of this) 2) are quoted but interfere
2337 with the users' own quotation. A more ideal solution is
2338 to refactor parse_command so that it doesn't just take a string
2339 and execute it; rather than that, we should have a function which
2340 returns the argument vector parsed from the string. This vector
2341 could be modified (e.g. insert additional args into it) before
2342 passing it to the next function that actually executes it. Though
2343 it still isn't perfect for chain actions.. will reconsider & re-
2344 factor when I have the time. -duc */
2346 char **parts = g_strsplit(act, " ", 2);
2348 if (g_strcmp0(parts[0], "chain") == 0) {
2349 GString *newargs = g_string_new("");
2350 gchar **chainparts = split_quoted(parts[1], FALSE);
2352 /* for every argument in the chain, inject the handler args
2353 and make sure the new parts are wrapped in quotes */
2354 gchar **cp = chainparts;
2356 gchar *quotless = NULL;
2357 gchar **spliced_quotless = NULL; // sigh -_-;
2358 gchar **inpart = NULL;
2361 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2363 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2364 } else quotless = g_strdup(*cp);
2366 spliced_quotless = g_strsplit(quotless, " ", 2);
2367 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2368 g_strfreev(spliced_quotless);
2370 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2376 parse_command(parts[0], &(newargs->str[1]), NULL);
2377 g_string_free(newargs, TRUE);
2378 g_strfreev(chainparts);
2381 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2382 parse_command(inparts[0], inparts[1], NULL);
2390 add_binding (const gchar *key, const gchar *act) {
2391 char **parts = g_strsplit(act, " ", 2);
2398 if (uzbl.state.verbose)
2399 printf ("Binding %-10s : %s\n", key, act);
2400 action = new_action(parts[0], parts[1]);
2402 if (g_hash_table_remove (uzbl.bindings, key))
2403 g_warning ("Overwriting existing binding for \"%s\"", key);
2404 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2409 get_xdg_var (XDG_Var xdg) {
2410 const gchar* actual_value = getenv (xdg.environmental);
2411 const gchar* home = getenv ("HOME");
2412 gchar* return_value;
2414 if (! actual_value || strcmp (actual_value, "") == 0) {
2415 if (xdg.default_value) {
2416 return_value = str_replace ("~", home, xdg.default_value);
2418 return_value = NULL;
2421 return_value = str_replace("~", home, actual_value);
2424 return return_value;
2428 find_xdg_file (int xdg_type, char* filename) {
2429 /* xdg_type = 0 => config
2430 xdg_type = 1 => data
2431 xdg_type = 2 => cache*/
2433 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2434 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2437 gchar* temporary_string;
2441 if (! file_exists (temporary_file) && xdg_type != 2) {
2442 buf = get_xdg_var (XDG[3 + xdg_type]);
2443 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2446 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2447 g_free (temporary_file);
2448 temporary_file = g_strconcat (temporary_string, filename, NULL);
2452 //g_free (temporary_string); - segfaults.
2454 if (file_exists (temporary_file)) {
2455 return temporary_file;
2462 State *s = &uzbl.state;
2463 Network *n = &uzbl.net;
2465 for (i = 0; default_config[i].command != NULL; i++) {
2466 parse_cmd_line(default_config[i].command, NULL);
2469 if (g_strcmp0(s->config_file, "-") == 0) {
2470 s->config_file = NULL;
2474 else if (!s->config_file) {
2475 s->config_file = find_xdg_file (0, "/uzbl/config");
2478 if (s->config_file) {
2479 GArray* lines = read_file_by_line (s->config_file);
2483 while ((line = g_array_index(lines, gchar*, i))) {
2484 parse_cmd_line (line, NULL);
2488 g_array_free (lines, TRUE);
2490 if (uzbl.state.verbose)
2491 printf ("No configuration file loaded.\n");
2494 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2497 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2500 if (!uzbl.behave.cookie_handler)
2503 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2504 GString *s = g_string_new ("");
2505 SoupURI * soup_uri = soup_message_get_uri(msg);
2506 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2507 run_handler(uzbl.behave.cookie_handler, s->str);
2509 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2510 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2511 if ( p != NULL ) *p = '\0';
2512 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2514 if (uzbl.comm.sync_stdout)
2515 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2517 g_string_free(s, TRUE);
2521 save_cookies (SoupMessage *msg, gpointer user_data){
2525 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2526 cookie = soup_cookie_to_set_cookie_header(ck->data);
2527 SoupURI * soup_uri = soup_message_get_uri(msg);
2528 GString *s = g_string_new ("");
2529 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2530 run_handler(uzbl.behave.cookie_handler, s->str);
2532 g_string_free(s, TRUE);
2537 /* --- WEBINSPECTOR --- */
2539 hide_window_cb(GtkWidget *widget, gpointer data) {
2542 gtk_widget_hide(widget);
2545 static WebKitWebView*
2546 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2549 (void) web_inspector;
2550 GtkWidget* scrolled_window;
2551 GtkWidget* new_web_view;
2554 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2555 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2556 G_CALLBACK(hide_window_cb), NULL);
2558 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2559 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2560 gtk_widget_show(g->inspector_window);
2562 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2563 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2564 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2565 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2566 gtk_widget_show(scrolled_window);
2568 new_web_view = webkit_web_view_new();
2569 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2571 return WEBKIT_WEB_VIEW(new_web_view);
2575 inspector_show_window_cb (WebKitWebInspector* inspector){
2577 gtk_widget_show(uzbl.gui.inspector_window);
2581 /* TODO: Add variables and code to make use of these functions */
2583 inspector_close_window_cb (WebKitWebInspector* inspector){
2589 inspector_attach_window_cb (WebKitWebInspector* inspector){
2595 inspector_detach_window_cb (WebKitWebInspector* inspector){
2601 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2607 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2613 set_up_inspector() {
2615 WebKitWebSettings *settings = view_settings();
2616 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2618 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2619 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2620 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2621 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2622 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2623 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2624 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2626 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2630 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2632 uzbl_cmdprop *c = v;
2637 if(c->type == TYPE_STR)
2638 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2639 else if(c->type == TYPE_INT)
2640 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2644 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2648 printf("bind %s = %s %s\n", (char *)k ,
2649 (char *)a->name, a->param?(char *)a->param:"");
2654 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2655 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2658 /* set up gtk, gobject, variable defaults and other things that tests and other
2659 * external applications need to do anyhow */
2661 initialize(int argc, char *argv[]) {
2662 gtk_init (&argc, &argv);
2663 if (!g_thread_supported ())
2664 g_thread_init (NULL);
2665 uzbl.state.executable_path = g_strdup(argv[0]);
2666 uzbl.state.selected_url = NULL;
2667 uzbl.state.searchtx = NULL;
2669 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2670 g_option_context_add_main_entries (context, entries, NULL);
2671 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2672 g_option_context_parse (context, &argc, &argv, NULL);
2673 g_option_context_free(context);
2675 if (uzbl.behave.print_version) {
2676 printf("Commit: %s\n", COMMIT);
2680 /* initialize hash table */
2681 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2683 uzbl.net.soup_session = webkit_get_default_session();
2684 uzbl.state.keycmd = g_string_new("");
2686 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2687 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2688 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2689 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2690 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2691 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2693 uzbl.gui.sbar.progress_s = g_strdup("=");
2694 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2695 uzbl.gui.sbar.progress_w = 10;
2697 /* HTML mode defaults*/
2698 uzbl.behave.html_buffer = g_string_new("");
2699 uzbl.behave.html_endmarker = g_strdup(".");
2700 uzbl.behave.html_timeout = 60;
2701 uzbl.behave.base_url = g_strdup("http://invalid");
2703 /* default mode indicators */
2704 uzbl.behave.insert_indicator = g_strdup("I");
2705 uzbl.behave.cmd_indicator = g_strdup("C");
2706 set_insert_mode(FALSE);
2708 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2709 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2710 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2711 uzbl.info.arch = ARCH;
2712 uzbl.info.commit = COMMIT;
2716 make_var_to_name_hash();
2718 uzbl.gui.scrolled_win = create_browser();
2721 #ifndef UZBL_LIBRARY
2724 main (int argc, char* argv[]) {
2725 initialize(argc, argv);
2727 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2731 /* initial packing */
2732 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2733 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2735 if (uzbl.state.socket_id) {
2736 uzbl.gui.plug = create_plug ();
2737 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2738 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2740 uzbl.gui.main_window = create_window ();
2741 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2742 gtk_widget_show_all (uzbl.gui.main_window);
2743 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2746 if(!uzbl.state.instance_name)
2747 uzbl.state.instance_name = itos((int)uzbl.xwin);
2749 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2751 if (uzbl.state.verbose) {
2752 printf("Uzbl start location: %s\n", argv[0]);
2753 if (uzbl.state.socket_id)
2754 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2756 printf("window_id %i\n",(int) uzbl.xwin);
2757 printf("pid %i\n", getpid ());
2758 printf("name: %s\n", uzbl.state.instance_name);
2761 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2762 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2763 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2764 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2765 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2769 if (!uzbl.behave.show_status)
2770 gtk_widget_hide(uzbl.gui.mainbar);
2777 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2778 gboolean verbose_override = uzbl.state.verbose;
2780 if (verbose_override > uzbl.state.verbose)
2781 uzbl.state.verbose = verbose_override;
2784 set_var_value("uri", uri_override);
2785 g_free(uri_override);
2786 } else if (uzbl.state.uri)
2787 cmd_load_uri(uzbl.gui.web_view, NULL);
2792 return EXIT_SUCCESS;
2796 /* vi: set et ts=4: */