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 { "MSG", PTR_C(uzbl.gui.sbar.msg, STR, NULL)},
170 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
171 }, *n2v_p = var_name_to_ptr;
178 { "SHIFT", GDK_SHIFT_MASK }, // shift
179 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
180 { "CONTROL", GDK_CONTROL_MASK }, // control
181 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
182 { "MOD2", GDK_MOD2_MASK }, // 5th mod
183 { "MOD3", GDK_MOD3_MASK }, // 6th mod
184 { "MOD4", GDK_MOD4_MASK }, // 7th mod
185 { "MOD5", GDK_MOD5_MASK }, // 8th mod
186 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
187 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
188 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
189 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
190 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
191 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
192 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
193 { "META", GDK_META_MASK }, // meta (since 2.10)
198 /* construct a hash from the var_name_to_ptr and the const_name_to_ptr array
199 * for quick access */
201 make_var_to_name_hash() {
202 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
204 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
209 /* --- UTILITY FUNCTIONS --- */
211 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
213 get_exp_type(gchar *s) {
217 else if(*(s+1) == '{')
218 return EXP_BRACED_VAR;
219 else if(*(s+1) == '<')
222 return EXP_SIMPLE_VAR;
228 * recurse == 1: don't expand '@(command)@'
229 * recurse == 2: don't expand '@<java script>@'
232 expand(char *s, guint recurse, gboolean escape_markup) {
236 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
241 gchar *cmd_stdout = NULL;
243 GString *buf = g_string_new("");
244 GString *js_ret = g_string_new("");
249 g_string_append_c(buf, *++s);
254 etype = get_exp_type(s);
259 if( (vend = strpbrk(s, end_simple_var)) ||
260 (vend = strchr(s, '\0')) ) {
261 strncpy(ret, s, vend-s);
267 if( (vend = strchr(s, upto)) ||
268 (vend = strchr(s, '\0')) ) {
269 strncpy(ret, s, vend-s);
275 strcpy(str_end, ")@");
277 if( (vend = strstr(s, str_end)) ||
278 (vend = strchr(s, '\0')) ) {
279 strncpy(ret, s, vend-s);
285 strcpy(str_end, ">@");
287 if( (vend = strstr(s, str_end)) ||
288 (vend = strchr(s, '\0')) ) {
289 strncpy(ret, s, vend-s);
295 if(etype == EXP_SIMPLE_VAR ||
296 etype == EXP_BRACED_VAR) {
297 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
298 if(c->type == TYPE_STR && *c->ptr != NULL) {
300 char *b = g_markup_escape_text((gchar *)*c->ptr,
301 strlen((gchar *)*c->ptr));
302 g_string_append(buf, b);
305 g_string_append(buf, (gchar *)*c->ptr);
307 } else if(c->type == TYPE_INT) {
308 char *b = itos((uintptr_t)*c->ptr);
309 g_string_append(buf, b);
314 if(etype == EXP_SIMPLE_VAR)
319 else if(recurse != 1 &&
321 mycmd = expand(ret, 1, escape_markup);
322 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
326 g_printerr("error on running command: %s\n", err->message);
329 else if (*cmd_stdout) {
330 int len = strlen(cmd_stdout);
332 if(cmd_stdout[len-1] == '\n')
333 cmd_stdout[--len] = 0; /* strip trailing newline */
336 char *b = g_markup_escape_text(cmd_stdout, len);
337 g_string_append(buf, b);
340 g_string_append(buf, cmd_stdout);
346 else if(recurse != 2 &&
348 mycmd = expand(ret, 2, escape_markup);
349 eval_js(uzbl.gui.web_view, mycmd, js_ret);
354 char *b = g_markup_escape_text(js_ret->str,
355 strlen(js_ret->str));
356 g_string_append(buf, b);
359 g_string_append(buf, js_ret->str);
361 g_string_free(js_ret, TRUE);
362 js_ret = g_string_new("");
369 g_string_append_c(buf, *s);
374 g_string_free(js_ret, TRUE);
375 return g_string_free(buf, FALSE);
382 snprintf(tmp, sizeof(tmp), "%i", val);
383 return g_strdup(tmp);
387 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
390 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
393 str_replace (const char* search, const char* replace, const char* string) {
397 buf = g_strsplit (string, search, -1);
398 ret = g_strjoinv (replace, buf);
399 g_strfreev(buf); // somebody said this segfaults
405 read_file_by_line (gchar *path) {
406 GIOChannel *chan = NULL;
407 gchar *readbuf = NULL;
409 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
412 chan = g_io_channel_new_file(path, "r", NULL);
415 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
416 const gchar* val = g_strdup (readbuf);
417 g_array_append_val (lines, val);
422 g_io_channel_unref (chan);
424 fprintf(stderr, "File '%s' not be read.\n", path);
431 gchar* parseenv (char* string) {
432 extern char** environ;
433 gchar* tmpstr = NULL;
437 while (environ[i] != NULL) {
438 gchar** env = g_strsplit (environ[i], "=", 2);
439 gchar* envname = g_strconcat ("$", env[0], NULL);
441 if (g_strrstr (string, envname) != NULL) {
442 tmpstr = g_strdup(string);
444 string = str_replace(envname, env[1], tmpstr);
449 g_strfreev (env); // somebody said this breaks uzbl
457 setup_signal(int signr, sigfunc *shandler) {
458 struct sigaction nh, oh;
460 nh.sa_handler = shandler;
461 sigemptyset(&nh.sa_mask);
464 if(sigaction(signr, &nh, &oh) < 0)
472 if (uzbl.behave.fifo_dir)
473 unlink (uzbl.comm.fifo_path);
474 if (uzbl.behave.socket_dir)
475 unlink (uzbl.comm.socket_path);
477 g_free(uzbl.state.executable_path);
478 g_string_free(uzbl.state.keycmd, TRUE);
479 g_hash_table_destroy(uzbl.bindings);
480 g_hash_table_destroy(uzbl.behave.commands);
483 /* used for html_mode_timeout
484 * be sure to extend this function to use
485 * more timers if needed in other places
488 set_timeout(int seconds) {
490 memset(&t, 0, sizeof t);
492 t.it_value.tv_sec = seconds;
493 t.it_value.tv_usec = 0;
494 setitimer(ITIMER_REAL, &t, NULL);
497 /* --- SIGNAL HANDLER --- */
500 catch_sigterm(int s) {
506 catch_sigint(int s) {
516 set_var_value("mode", "0");
521 /* --- CALLBACKS --- */
524 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
527 (void) navigation_action;
528 (void) policy_decision;
530 const gchar* uri = webkit_network_request_get_uri (request);
531 if (uzbl.state.verbose)
532 printf("New window requested -> %s \n", uri);
533 new_window_load_uri(uri);
538 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
543 /* If we can display it, let's display it... */
544 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
545 webkit_web_policy_decision_use (policy_decision);
549 /* ...everything we can't displayed is downloaded */
550 webkit_web_policy_decision_download (policy_decision);
555 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
559 if (uzbl.state.selected_url != NULL) {
560 if (uzbl.state.verbose)
561 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
562 new_window_load_uri(uzbl.state.selected_url);
564 if (uzbl.state.verbose)
565 printf("New web view -> %s\n","Nothing to open, exiting");
571 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
574 if (uzbl.behave.download_handler) {
575 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
576 if (uzbl.state.verbose)
577 printf("Download -> %s\n",uri);
578 /* if urls not escaped, we may have to escape and quote uri before this call */
579 run_handler(uzbl.behave.download_handler, uri);
584 /* scroll a bar in a given direction */
586 scroll (GtkAdjustment* bar, GArray *argv) {
590 gdouble page_size = gtk_adjustment_get_page_size(bar);
591 gdouble value = gtk_adjustment_get_value(bar);
592 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
595 value += page_size * amount * 0.01;
599 max_value = gtk_adjustment_get_upper(bar) - page_size;
601 if (value > max_value)
602 value = max_value; /* don't scroll past the end of the page */
604 gtk_adjustment_set_value (bar, value);
608 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
609 (void) page; (void) argv; (void) result;
610 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
614 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
615 (void) page; (void) argv; (void) result;
616 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
617 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
621 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
622 (void) page; (void) result;
623 scroll(uzbl.gui.bar_v, argv);
627 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
628 (void) page; (void) result;
629 scroll(uzbl.gui.bar_h, argv);
634 if (!uzbl.behave.show_status) {
635 gtk_widget_hide(uzbl.gui.mainbar);
637 gtk_widget_show(uzbl.gui.mainbar);
643 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
648 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
652 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
657 if (uzbl.behave.show_status) {
658 gtk_widget_hide(uzbl.gui.mainbar);
660 gtk_widget_show(uzbl.gui.mainbar);
662 uzbl.behave.show_status = !uzbl.behave.show_status;
667 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
671 //Set selected_url state variable
672 g_free(uzbl.state.selected_url);
673 uzbl.state.selected_url = NULL;
675 uzbl.state.selected_url = g_strdup(link);
681 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
684 const gchar *title = webkit_web_view_get_title(web_view);
685 if (uzbl.gui.main_title)
686 g_free (uzbl.gui.main_title);
687 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
692 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
695 uzbl.gui.sbar.load_progress = progress;
697 g_free(uzbl.gui.sbar.progress_bar);
698 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
704 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
708 if (uzbl.behave.load_finish_handler)
709 run_handler(uzbl.behave.load_finish_handler, "");
713 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
717 uzbl.gui.sbar.load_progress = 0;
718 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
719 if (uzbl.behave.load_start_handler)
720 run_handler(uzbl.behave.load_start_handler, "");
724 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
727 g_free (uzbl.state.uri);
728 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
729 uzbl.state.uri = g_string_free (newuri, FALSE);
730 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
731 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
734 if (uzbl.behave.load_commit_handler)
735 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
739 destroy_cb (GtkWidget* widget, gpointer data) {
747 if (uzbl.behave.history_handler) {
749 struct tm * timeinfo;
752 timeinfo = localtime ( &rawtime );
753 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
754 run_handler(uzbl.behave.history_handler, date);
759 /* VIEW funcs (little webkit wrappers) */
760 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
762 VIEWFUNC(reload_bypass_cache)
763 VIEWFUNC(stop_loading)
770 /* -- command to callback/function map for things we cannot attach to any signals */
771 static struct {char *key; CommandInfo value;} cmdlist[] =
772 { /* key function no_split */
773 { "back", {view_go_back, 0} },
774 { "forward", {view_go_forward, 0} },
775 { "scroll_vert", {scroll_vert, 0} },
776 { "scroll_horz", {scroll_horz, 0} },
777 { "scroll_begin", {scroll_begin, 0} },
778 { "scroll_end", {scroll_end, 0} },
779 { "reload", {view_reload, 0}, },
780 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
781 { "stop", {view_stop_loading, 0}, },
782 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
783 { "zoom_out", {view_zoom_out, 0}, },
784 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
785 { "uri", {load_uri, TRUE} },
786 { "js", {run_js, TRUE} },
787 { "script", {run_external_js, 0} },
788 { "toggle_status", {toggle_status_cb, 0} },
789 { "spawn", {spawn, 0} },
790 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
791 { "sh", {spawn_sh, 0} },
792 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
793 { "exit", {close_uzbl, 0} },
794 { "search", {search_forward_text, TRUE} },
795 { "search_reverse", {search_reverse_text, TRUE} },
796 { "dehilight", {dehilight, 0} },
797 { "toggle_insert_mode", {toggle_insert_mode, 0} },
798 { "set", {set_var, TRUE} },
799 //{ "get", {get_var, TRUE} },
800 { "bind", {act_bind, TRUE} },
801 { "dump_config", {act_dump_config, 0} },
802 { "keycmd", {keycmd, TRUE} },
803 { "keycmd_nl", {keycmd_nl, TRUE} },
804 { "keycmd_bs", {keycmd_bs, 0} },
805 { "chain", {chain, 0} },
806 { "print", {print, TRUE} }
813 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
815 for (i = 0; i < LENGTH(cmdlist); i++)
816 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
819 /* -- CORE FUNCTIONS -- */
822 free_action(gpointer act) {
823 Action *action = (Action*)act;
824 g_free(action->name);
826 g_free(action->param);
831 new_action(const gchar *name, const gchar *param) {
832 Action *action = g_new(Action, 1);
834 action->name = g_strdup(name);
836 action->param = g_strdup(param);
838 action->param = NULL;
844 file_exists (const char * filename) {
845 return (access(filename, F_OK) == 0);
849 set_var(WebKitWebView *page, GArray *argv, GString *result) {
850 (void) page; (void) result;
851 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
852 if (split[0] != NULL) {
853 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
854 set_var_value(g_strstrip(split[0]), value);
861 print(WebKitWebView *page, GArray *argv, GString *result) {
862 (void) page; (void) result;
865 buf = expand(argv_idx(argv, 0), 0, FALSE);
866 g_string_assign(result, buf);
871 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
872 (void) page; (void) result;
873 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
874 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
875 add_binding(g_strstrip(split[0]), value);
887 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
888 (void) page; (void) result;
890 if (argv_idx(argv, 0)) {
891 if (strcmp (argv_idx(argv, 0), "0") == 0) {
892 uzbl.behave.insert_mode = FALSE;
894 uzbl.behave.insert_mode = TRUE;
897 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
904 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
907 if (argv_idx(argv, 0)) {
908 GString* newuri = g_string_new (argv_idx(argv, 0));
909 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
910 run_js(web_view, argv, NULL);
913 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
914 g_string_prepend (newuri, "http://");
915 /* if we do handle cookies, ask our handler for them */
916 webkit_web_view_load_uri (web_view, newuri->str);
917 g_string_free (newuri, TRUE);
925 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
926 size_t argumentCount, const JSValueRef arguments[],
927 JSValueRef* exception) {
932 JSStringRef js_result_string;
933 GString *result = g_string_new("");
935 if (argumentCount >= 1) {
936 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
937 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
938 char ctl_line[arg_size];
939 JSStringGetUTF8CString(arg, ctl_line, arg_size);
941 parse_cmd_line(ctl_line, result);
943 JSStringRelease(arg);
945 js_result_string = JSStringCreateWithUTF8CString(result->str);
947 g_string_free(result, TRUE);
949 return JSValueMakeString(ctx, js_result_string);
952 static JSStaticFunction js_static_functions[] = {
953 {"run", js_run_command, kJSPropertyAttributeNone},
958 /* This function creates the class and its definition, only once */
959 if (!uzbl.js.initialized) {
960 /* it would be pretty cool to make this dynamic */
961 uzbl.js.classdef = kJSClassDefinitionEmpty;
962 uzbl.js.classdef.staticFunctions = js_static_functions;
964 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
970 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
971 WebKitWebFrame *frame;
972 JSGlobalContextRef context;
973 JSObjectRef globalobject;
974 JSStringRef var_name;
976 JSStringRef js_script;
977 JSValueRef js_result;
978 JSStringRef js_result_string;
979 size_t js_result_size;
983 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
984 context = webkit_web_frame_get_global_context(frame);
985 globalobject = JSContextGetGlobalObject(context);
987 /* uzbl javascript namespace */
988 var_name = JSStringCreateWithUTF8CString("Uzbl");
989 JSObjectSetProperty(context, globalobject, var_name,
990 JSObjectMake(context, uzbl.js.classref, NULL),
991 kJSClassAttributeNone, NULL);
993 /* evaluate the script and get return value*/
994 js_script = JSStringCreateWithUTF8CString(script);
995 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
996 if (js_result && !JSValueIsUndefined(context, js_result)) {
997 js_result_string = JSValueToStringCopy(context, js_result, NULL);
998 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1000 if (js_result_size) {
1001 char js_result_utf8[js_result_size];
1002 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1003 g_string_assign(result, js_result_utf8);
1006 JSStringRelease(js_result_string);
1010 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1012 JSStringRelease(var_name);
1013 JSStringRelease(js_script);
1017 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1019 if (argv_idx(argv, 0))
1020 eval_js(web_view, argv_idx(argv, 0), result);
1024 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1026 if (argv_idx(argv, 0)) {
1027 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1032 while ((line = g_array_index(lines, gchar*, i))) {
1034 js = g_strdup (line);
1036 gchar* newjs = g_strconcat (js, line, NULL);
1043 if (uzbl.state.verbose)
1044 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1046 if (argv_idx (argv, 1)) {
1047 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1051 eval_js (web_view, js, result);
1053 g_array_free (lines, TRUE);
1058 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1059 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1060 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1061 webkit_web_view_unmark_text_matches (page);
1062 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1063 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1067 if (uzbl.state.searchtx) {
1068 if (uzbl.state.verbose)
1069 printf ("Searching: %s\n", uzbl.state.searchtx);
1070 webkit_web_view_set_highlight_text_matches (page, TRUE);
1071 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1076 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1078 search_text(page, argv, TRUE);
1082 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1084 search_text(page, argv, FALSE);
1088 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1089 (void) argv; (void) result;
1090 webkit_web_view_set_highlight_text_matches (page, FALSE);
1095 new_window_load_uri (const gchar * uri) {
1096 GString* to_execute = g_string_new ("");
1097 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1099 for (i = 0; entries[i].long_name != NULL; i++) {
1100 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1101 gchar** str = (gchar**)entries[i].arg_data;
1103 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1107 if (uzbl.state.verbose)
1108 printf("\n%s\n", to_execute->str);
1109 g_spawn_command_line_async (to_execute->str, NULL);
1110 g_string_free (to_execute, TRUE);
1114 chain (WebKitWebView *page, GArray *argv, GString *result) {
1115 (void) page; (void) result;
1117 gchar **parts = NULL;
1119 while ((a = argv_idx(argv, i++))) {
1120 parts = g_strsplit (a, " ", 2);
1121 parse_command(parts[0], parts[1], result);
1127 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1131 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1137 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1141 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1147 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1152 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1154 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1159 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1166 /* --Statusbar functions-- */
1168 build_progressbar_ascii(int percent) {
1169 int width=uzbl.gui.sbar.progress_w;
1172 GString *bar = g_string_new("");
1174 l = (double)percent*((double)width/100.);
1175 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1177 for(i=0; i<(int)l; i++)
1178 g_string_append(bar, uzbl.gui.sbar.progress_s);
1181 g_string_append(bar, uzbl.gui.sbar.progress_u);
1183 return g_string_free(bar, FALSE);
1188 const GScannerConfig scan_config = {
1191 ) /* cset_skip_characters */,
1196 ) /* cset_identifier_first */,
1203 ) /* cset_identifier_nth */,
1204 ( "" ) /* cpair_comment_single */,
1206 TRUE /* case_sensitive */,
1208 FALSE /* skip_comment_multi */,
1209 FALSE /* skip_comment_single */,
1210 FALSE /* scan_comment_multi */,
1211 TRUE /* scan_identifier */,
1212 TRUE /* scan_identifier_1char */,
1213 FALSE /* scan_identifier_NULL */,
1214 TRUE /* scan_symbols */,
1215 FALSE /* scan_binary */,
1216 FALSE /* scan_octal */,
1217 FALSE /* scan_float */,
1218 FALSE /* scan_hex */,
1219 FALSE /* scan_hex_dollar */,
1220 FALSE /* scan_string_sq */,
1221 FALSE /* scan_string_dq */,
1222 TRUE /* numbers_2_int */,
1223 FALSE /* int_2_float */,
1224 FALSE /* identifier_2_string */,
1225 FALSE /* char_2_token */,
1226 FALSE /* symbol_2_token */,
1227 TRUE /* scope_0_fallback */,
1232 uzbl.scan = g_scanner_new(&scan_config);
1233 while(symp->symbol_name) {
1234 g_scanner_scope_add_symbol(uzbl.scan, 0,
1236 GINT_TO_POINTER(symp->symbol_token));
1242 expand_template(const char *template, gboolean escape_markup) {
1243 if(!template) return NULL;
1245 GTokenType token = G_TOKEN_NONE;
1246 GString *ret = g_string_new("");
1250 g_scanner_input_text(uzbl.scan, template, strlen(template));
1251 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1252 token = g_scanner_get_next_token(uzbl.scan);
1254 if(token == G_TOKEN_SYMBOL) {
1255 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1258 buf = itos(uzbl.xwin);
1259 g_string_append(ret,
1260 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1265 buf = uzbl.state.keycmd->str?
1266 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1267 g_string_append(ret, buf);
1271 g_string_append(ret, uzbl.state.keycmd->str?
1272 uzbl.state.keycmd->str:g_strdup(""));
1275 g_string_append(ret,
1276 uzbl.behave.insert_mode?
1277 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1283 else if(token == G_TOKEN_INT) {
1284 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1285 g_string_append(ret, buf);
1288 else if(token == G_TOKEN_IDENTIFIER) {
1289 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1291 else if(token == G_TOKEN_CHAR) {
1292 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1296 return g_string_free(ret, FALSE);
1298 /* --End Statusbar functions-- */
1301 sharg_append(GArray *a, const gchar *str) {
1302 const gchar *s = (str ? str : "");
1303 g_array_append_val(a, s);
1306 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1308 run_command (const gchar *command, const guint npre, const gchar **args,
1309 const gboolean sync, char **output_stdout) {
1310 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1313 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1314 gchar *pid = itos(getpid());
1315 gchar *xwin = itos(uzbl.xwin);
1317 sharg_append(a, command);
1318 for (i = 0; i < npre; i++) /* add n args before the default vars */
1319 sharg_append(a, args[i]);
1320 sharg_append(a, uzbl.state.config_file);
1321 sharg_append(a, pid);
1322 sharg_append(a, xwin);
1323 sharg_append(a, uzbl.comm.fifo_path);
1324 sharg_append(a, uzbl.comm.socket_path);
1325 sharg_append(a, uzbl.state.uri);
1326 sharg_append(a, uzbl.gui.main_title);
1328 for (i = npre; i < g_strv_length((gchar**)args); i++)
1329 sharg_append(a, args[i]);
1333 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1335 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1336 NULL, NULL, output_stdout, NULL, NULL, &err);
1337 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1338 NULL, NULL, NULL, &err);
1340 if (uzbl.state.verbose) {
1341 GString *s = g_string_new("spawned:");
1342 for (i = 0; i < (a->len); i++) {
1343 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1344 g_string_append_printf(s, " %s", qarg);
1347 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1348 printf("%s\n", s->str);
1349 g_string_free(s, TRUE);
1351 printf("Stdout: %s\n", *output_stdout);
1355 g_printerr("error on run_command: %s\n", err->message);
1360 g_array_free (a, TRUE);
1365 split_quoted(const gchar* src, const gboolean unquote) {
1366 /* split on unquoted space, return array of strings;
1367 remove a layer of quotes and backslashes if unquote */
1368 if (!src) return NULL;
1370 gboolean dq = FALSE;
1371 gboolean sq = FALSE;
1372 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1373 GString *s = g_string_new ("");
1377 for (p = src; *p != '\0'; p++) {
1378 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1379 else if (*p == '\\') { g_string_append_c(s, *p++);
1380 g_string_append_c(s, *p); }
1381 else if ((*p == '"') && unquote && !sq) dq = !dq;
1382 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1384 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1385 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1387 else if ((*p == ' ') && !dq && !sq) {
1388 dup = g_strdup(s->str);
1389 g_array_append_val(a, dup);
1390 g_string_truncate(s, 0);
1391 } else g_string_append_c(s, *p);
1393 dup = g_strdup(s->str);
1394 g_array_append_val(a, dup);
1395 ret = (gchar**)a->data;
1396 g_array_free (a, FALSE);
1397 g_string_free (s, TRUE);
1402 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1403 (void)web_view; (void)result;
1404 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1405 if (argv_idx(argv, 0))
1406 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1410 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1411 (void)web_view; (void)result;
1413 if (argv_idx(argv, 0))
1414 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1415 TRUE, &uzbl.comm.sync_stdout);
1419 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1420 (void)web_view; (void)result;
1421 if (!uzbl.behave.shell_cmd) {
1422 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1427 gchar *spacer = g_strdup("");
1428 g_array_insert_val(argv, 1, spacer);
1429 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1431 for (i = 1; i < g_strv_length(cmd); i++)
1432 g_array_prepend_val(argv, cmd[i]);
1434 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1440 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1441 (void)web_view; (void)result;
1442 if (!uzbl.behave.shell_cmd) {
1443 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1448 gchar *spacer = g_strdup("");
1449 g_array_insert_val(argv, 1, spacer);
1450 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1452 for (i = 1; i < g_strv_length(cmd); i++)
1453 g_array_prepend_val(argv, cmd[i]);
1455 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1456 TRUE, &uzbl.comm.sync_stdout);
1462 parse_command(const char *cmd, const char *param, GString *result) {
1465 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1467 gchar **par = split_quoted(param, TRUE);
1468 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1470 if (c->no_split) { /* don't split */
1471 sharg_append(a, param);
1473 for (i = 0; i < g_strv_length(par); i++)
1474 sharg_append(a, par[i]);
1477 if (result == NULL) {
1478 GString *result_print = g_string_new("");
1480 c->function(uzbl.gui.web_view, a, result_print);
1481 if (result_print->len)
1482 printf("%*s\n", result_print->len, result_print->str);
1484 g_string_free(result_print, TRUE);
1486 c->function(uzbl.gui.web_view, a, result);
1489 g_array_free (a, TRUE);
1492 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1499 if(*uzbl.net.proxy_url == ' '
1500 || uzbl.net.proxy_url == NULL) {
1501 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1502 (GType) SOUP_SESSION_PROXY_URI);
1505 suri = soup_uri_new(uzbl.net.proxy_url);
1506 g_object_set(G_OBJECT(uzbl.net.soup_session),
1507 SOUP_SESSION_PROXY_URI,
1509 soup_uri_free(suri);
1516 if(file_exists(uzbl.gui.icon)) {
1517 if (uzbl.gui.main_window)
1518 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1520 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1522 g_free (uzbl.gui.icon);
1527 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1528 g_array_append_val (a, uzbl.state.uri);
1529 load_uri(uzbl.gui.web_view, a, NULL);
1530 g_array_free (a, TRUE);
1534 cmd_always_insert_mode() {
1535 uzbl.behave.insert_mode =
1536 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1542 g_object_set(G_OBJECT(uzbl.net.soup_session),
1543 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1547 cmd_max_conns_host() {
1548 g_object_set(G_OBJECT(uzbl.net.soup_session),
1549 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1554 soup_session_remove_feature
1555 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1556 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1557 /*g_free(uzbl.net.soup_logger);*/
1559 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1560 soup_session_add_feature(uzbl.net.soup_session,
1561 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1564 static WebKitWebSettings*
1566 return webkit_web_view_get_settings(uzbl.gui.web_view);
1571 WebKitWebSettings *ws = view_settings();
1572 if (uzbl.behave.font_size > 0) {
1573 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1576 if (uzbl.behave.monospace_size > 0) {
1577 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1578 uzbl.behave.monospace_size, NULL);
1580 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1581 uzbl.behave.font_size, NULL);
1587 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1591 cmd_disable_plugins() {
1592 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1593 !uzbl.behave.disable_plugins, NULL);
1597 cmd_disable_scripts() {
1598 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1599 !uzbl.behave.disable_scripts, NULL);
1603 cmd_minimum_font_size() {
1604 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1605 uzbl.behave.minimum_font_size, NULL);
1608 cmd_autoload_img() {
1609 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1610 uzbl.behave.autoload_img, NULL);
1615 cmd_autoshrink_img() {
1616 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1617 uzbl.behave.autoshrink_img, NULL);
1622 cmd_enable_spellcheck() {
1623 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1624 uzbl.behave.enable_spellcheck, NULL);
1628 cmd_enable_private() {
1629 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1630 uzbl.behave.enable_private, NULL);
1635 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1636 uzbl.behave.print_bg, NULL);
1641 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1642 uzbl.behave.style_uri, NULL);
1646 cmd_resizable_txt() {
1647 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1648 uzbl.behave.resizable_txt, NULL);
1652 cmd_default_encoding() {
1653 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1654 uzbl.behave.default_encoding, NULL);
1658 cmd_enforce_96dpi() {
1659 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1660 uzbl.behave.enforce_96dpi, NULL);
1664 cmd_caret_browsing() {
1665 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1666 uzbl.behave.caret_browsing, NULL);
1670 cmd_cookie_handler() {
1671 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1672 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1673 if ((g_strcmp0(split[0], "sh") == 0) ||
1674 (g_strcmp0(split[0], "spawn") == 0)) {
1675 g_free (uzbl.behave.cookie_handler);
1676 uzbl.behave.cookie_handler =
1677 g_strdup_printf("sync_%s %s", split[0], split[1]);
1684 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1689 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1694 if(uzbl.behave.inject_html) {
1695 webkit_web_view_load_html_string (uzbl.gui.web_view,
1696 uzbl.behave.inject_html, NULL);
1705 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1706 uzbl.behave.modmask = 0;
1708 if(uzbl.behave.modkey)
1709 g_free(uzbl.behave.modkey);
1710 uzbl.behave.modkey = buf;
1712 for (i = 0; modkeys[i].key != NULL; i++) {
1713 if (g_strrstr(buf, modkeys[i].key))
1714 uzbl.behave.modmask |= modkeys[i].mask;
1720 if (*uzbl.net.useragent == ' ') {
1721 g_free (uzbl.net.useragent);
1722 uzbl.net.useragent = NULL;
1724 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1725 uzbl.net.useragent, NULL);
1731 gtk_widget_ref(uzbl.gui.scrolled_win);
1732 gtk_widget_ref(uzbl.gui.mainbar);
1733 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1734 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1736 if(uzbl.behave.status_top) {
1737 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1738 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1741 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1742 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1744 gtk_widget_unref(uzbl.gui.scrolled_win);
1745 gtk_widget_unref(uzbl.gui.mainbar);
1746 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1751 set_var_value(gchar *name, gchar *val) {
1752 uzbl_cmdprop *c = NULL;
1756 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1757 if(!c->writeable) return TRUE;
1759 /* check for the variable type */
1760 if (c->type == TYPE_STR) {
1761 buf = expand(val, 0, FALSE);
1764 } else if(c->type == TYPE_INT) {
1765 int *ip = (int *)c->ptr;
1766 buf = expand(val, 0, FALSE);
1767 *ip = (int)strtoul(buf, &endp, 10);
1769 } else if (c->type == TYPE_FLOAT) {
1770 float *fp = (float *)c->ptr;
1771 buf = expand(val, 0, FALSE);
1772 *fp = strtod(buf, &endp);
1776 /* invoke a command specific function */
1777 if(c->func) c->func();
1784 Behaviour *b = &uzbl.behave;
1786 if(b->html_buffer->str) {
1787 webkit_web_view_load_html_string (uzbl.gui.web_view,
1788 b->html_buffer->str, b->base_url);
1789 g_string_free(b->html_buffer, TRUE);
1790 b->html_buffer = g_string_new("");
1794 enum {M_CMD, M_HTML};
1796 parse_cmd_line(const char *ctl_line, GString *result) {
1797 Behaviour *b = &uzbl.behave;
1800 if(b->mode == M_HTML) {
1801 len = strlen(b->html_endmarker);
1802 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1803 if(len == strlen(ctl_line)-1 &&
1804 !strncmp(b->html_endmarker, ctl_line, len)) {
1806 set_var_value("mode", "0");
1811 set_timeout(b->html_timeout);
1812 g_string_append(b->html_buffer, ctl_line);
1815 else if((ctl_line[0] == '#') /* Comments */
1816 || (ctl_line[0] == ' ')
1817 || (ctl_line[0] == '\n'))
1818 ; /* ignore these lines */
1819 else { /* parse a command */
1821 gchar **tokens = NULL;
1822 len = strlen(ctl_line);
1824 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1825 ctlstrip = g_strndup(ctl_line, len - 1);
1826 else ctlstrip = g_strdup(ctl_line);
1828 tokens = g_strsplit(ctlstrip, " ", 2);
1829 parse_command(tokens[0], tokens[1], result);
1836 build_stream_name(int type, const gchar* dir) {
1837 char *xwin_str = NULL;
1838 State *s = &uzbl.state;
1841 xwin_str = itos((int)uzbl.xwin);
1843 str = g_strdup_printf
1844 ("%s/uzbl_fifo_%s", dir,
1845 s->instance_name ? s->instance_name : xwin_str);
1846 } else if (type == SOCKET) {
1847 str = g_strdup_printf
1848 ("%s/uzbl_socket_%s", dir,
1849 s->instance_name ? s->instance_name : xwin_str );
1856 control_fifo(GIOChannel *gio, GIOCondition condition) {
1857 if (uzbl.state.verbose)
1858 printf("triggered\n");
1863 if (condition & G_IO_HUP)
1864 g_error ("Fifo: Read end of pipe died!\n");
1867 g_error ("Fifo: GIOChannel broke\n");
1869 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1870 if (ret == G_IO_STATUS_ERROR) {
1871 g_error ("Fifo: Error reading: %s\n", err->message);
1875 parse_cmd_line(ctl_line, NULL);
1882 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1883 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1884 if (unlink(uzbl.comm.fifo_path) == -1)
1885 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1886 g_free(uzbl.comm.fifo_path);
1887 uzbl.comm.fifo_path = NULL;
1890 if (*dir == ' ') { /* space unsets the variable */
1895 GIOChannel *chan = NULL;
1896 GError *error = NULL;
1897 gchar *path = build_stream_name(FIFO, dir);
1899 if (!file_exists(path)) {
1900 if (mkfifo (path, 0666) == 0) {
1901 // 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.
1902 chan = g_io_channel_new_file(path, "r+", &error);
1904 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1905 if (uzbl.state.verbose)
1906 printf ("init_fifo: created successfully as %s\n", path);
1907 uzbl.comm.fifo_path = path;
1909 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1910 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1911 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1912 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1914 /* if we got this far, there was an error; cleanup */
1915 if (error) g_error_free (error);
1922 control_stdin(GIOChannel *gio, GIOCondition condition) {
1924 gchar *ctl_line = NULL;
1927 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1928 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1931 parse_cmd_line(ctl_line, NULL);
1939 GIOChannel *chan = NULL;
1940 GError *error = NULL;
1942 chan = g_io_channel_unix_new(fileno(stdin));
1944 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1945 g_error ("Stdin: could not add watch\n");
1947 if (uzbl.state.verbose)
1948 printf ("Stdin: watch added successfully\n");
1951 g_error ("Stdin: Error while opening: %s\n", error->message);
1953 if (error) g_error_free (error);
1957 control_socket(GIOChannel *chan) {
1958 struct sockaddr_un remote;
1959 unsigned int t = sizeof(remote);
1961 GIOChannel *clientchan;
1963 clientsock = accept (g_io_channel_unix_get_fd(chan),
1964 (struct sockaddr *) &remote, &t);
1966 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1967 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1968 (GIOFunc) control_client_socket, clientchan);
1975 control_client_socket(GIOChannel *clientchan) {
1977 GString *result = g_string_new("");
1978 GError *error = NULL;
1982 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1983 if (ret == G_IO_STATUS_ERROR) {
1984 g_warning ("Error reading: %s\n", error->message);
1985 g_io_channel_shutdown(clientchan, TRUE, &error);
1987 } else if (ret == G_IO_STATUS_EOF) {
1988 /* shutdown and remove channel watch from main loop */
1989 g_io_channel_shutdown(clientchan, TRUE, &error);
1994 parse_cmd_line (ctl_line, result);
1995 g_string_append_c(result, '\n');
1996 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1998 if (ret == G_IO_STATUS_ERROR) {
1999 g_warning ("Error writing: %s", error->message);
2001 g_io_channel_flush(clientchan, &error);
2004 if (error) g_error_free (error);
2005 g_string_free(result, TRUE);
2011 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2012 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2013 if (unlink(uzbl.comm.socket_path) == -1)
2014 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2015 g_free(uzbl.comm.socket_path);
2016 uzbl.comm.socket_path = NULL;
2024 GIOChannel *chan = NULL;
2026 struct sockaddr_un local;
2027 gchar *path = build_stream_name(SOCKET, dir);
2029 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2031 local.sun_family = AF_UNIX;
2032 strcpy (local.sun_path, path);
2033 unlink (local.sun_path);
2035 len = strlen (local.sun_path) + sizeof (local.sun_family);
2036 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2037 if (uzbl.state.verbose)
2038 printf ("init_socket: opened in %s\n", path);
2041 if( (chan = g_io_channel_unix_new(sock)) ) {
2042 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2043 uzbl.comm.socket_path = path;
2046 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2048 /* if we got this far, there was an error; cleanup */
2055 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2056 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2058 // this function may be called very early when the templates are not set (yet), hence the checks
2060 update_title (void) {
2061 Behaviour *b = &uzbl.behave;
2064 if (b->show_status) {
2065 if (b->title_format_short) {
2066 parsed = expand_template(b->title_format_short, FALSE);
2067 if (uzbl.gui.main_window)
2068 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2071 if (b->status_format) {
2072 parsed = expand_template(b->status_format, TRUE);
2073 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2076 if (b->status_background) {
2078 gdk_color_parse (b->status_background, &color);
2079 //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)
2080 if (uzbl.gui.main_window)
2081 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2082 else if (uzbl.gui.plug)
2083 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2086 if (b->title_format_long) {
2087 parsed = expand_template(b->title_format_long, FALSE);
2088 if (uzbl.gui.main_window)
2089 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2096 key_press_cb (GtkWidget* window, GdkEventKey* event)
2098 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2102 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2103 || 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)
2106 /* turn off insert mode (if always_insert_mode is not used) */
2107 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2108 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2113 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2116 if (event->keyval == GDK_Escape) {
2117 g_string_truncate(uzbl.state.keycmd, 0);
2119 dehilight(uzbl.gui.web_view, NULL, NULL);
2123 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2124 if (event->keyval == GDK_Insert) {
2126 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2127 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2129 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2132 g_string_append (uzbl.state.keycmd, str);
2139 if (event->keyval == GDK_BackSpace)
2140 keycmd_bs(NULL, NULL, NULL);
2142 gboolean key_ret = FALSE;
2143 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2145 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2147 run_keycmd(key_ret);
2149 if (key_ret) return (!uzbl.behave.insert_mode);
2154 run_keycmd(const gboolean key_ret) {
2155 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2157 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2158 g_string_truncate(uzbl.state.keycmd, 0);
2159 parse_command(act->name, act->param, NULL);
2163 /* try if it's an incremental keycmd or one that takes args, and run it */
2164 GString* short_keys = g_string_new ("");
2165 GString* short_keys_inc = g_string_new ("");
2167 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2168 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2169 g_string_assign(short_keys_inc, short_keys->str);
2170 g_string_append_c(short_keys, '_');
2171 g_string_append_c(short_keys_inc, '*');
2173 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2174 /* run normal cmds only if return was pressed */
2175 exec_paramcmd(act, i);
2176 g_string_truncate(uzbl.state.keycmd, 0);
2178 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2179 if (key_ret) /* just quit the incremental command on return */
2180 g_string_truncate(uzbl.state.keycmd, 0);
2181 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2185 g_string_truncate(short_keys, short_keys->len - 1);
2187 g_string_free (short_keys, TRUE);
2188 g_string_free (short_keys_inc, TRUE);
2192 exec_paramcmd(const Action *act, const guint i) {
2193 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2194 GString *actionname = g_string_new ("");
2195 GString *actionparam = g_string_new ("");
2196 g_string_erase (parampart, 0, i+1);
2198 g_string_printf (actionname, act->name, parampart->str);
2200 g_string_printf (actionparam, act->param, parampart->str);
2201 parse_command(actionname->str, actionparam->str, NULL);
2202 g_string_free(actionname, TRUE);
2203 g_string_free(actionparam, TRUE);
2204 g_string_free(parampart, TRUE);
2212 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2213 //main_window_ref = g_object_ref(scrolled_window);
2214 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
2216 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2217 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2219 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2220 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2221 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2222 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2223 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2224 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2225 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2226 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2227 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2228 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2229 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2231 return scrolled_window;
2238 g->mainbar = gtk_hbox_new (FALSE, 0);
2240 /* keep a reference to the bar so we can re-pack it at runtime*/
2241 //sbar_ref = g_object_ref(g->mainbar);
2243 g->mainbar_label = gtk_label_new ("");
2244 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2245 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2246 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2247 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2248 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2249 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2254 GtkWidget* create_window () {
2255 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2256 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2257 gtk_widget_set_name (window, "Uzbl browser");
2258 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2259 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2265 GtkPlug* create_plug () {
2266 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2267 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2268 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2275 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2277 If actname is one that calls an external command, this function will inject
2278 newargs in front of the user-provided args in that command line. They will
2279 come become after the body of the script (in sh) or after the name of
2280 the command to execute (in spawn).
2281 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2282 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2284 The return value consist of two strings: the action (sh, ...) and its args.
2286 If act is not one that calls an external command, then the given action merely
2289 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2290 gchar *actdup = g_strdup(actname);
2291 g_array_append_val(rets, actdup);
2293 if ((g_strcmp0(actname, "spawn") == 0) ||
2294 (g_strcmp0(actname, "sh") == 0) ||
2295 (g_strcmp0(actname, "sync_spawn") == 0) ||
2296 (g_strcmp0(actname, "sync_sh") == 0)) {
2298 GString *a = g_string_new("");
2299 gchar **spawnparts = split_quoted(origargs, FALSE);
2300 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2301 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2303 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2304 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2306 g_array_append_val(rets, a->str);
2307 g_string_free(a, FALSE);
2308 g_strfreev(spawnparts);
2310 gchar *origdup = g_strdup(origargs);
2311 g_array_append_val(rets, origdup);
2313 return (gchar**)g_array_free(rets, FALSE);
2317 run_handler (const gchar *act, const gchar *args) {
2318 /* Consider this code a temporary hack to make the handlers usable.
2319 In practice, all this splicing, injection, and reconstruction is
2320 inefficient, annoying and hard to manage. Potential pitfalls arise
2321 when the handler specific args 1) are not quoted (the handler
2322 callbacks should take care of this) 2) are quoted but interfere
2323 with the users' own quotation. A more ideal solution is
2324 to refactor parse_command so that it doesn't just take a string
2325 and execute it; rather than that, we should have a function which
2326 returns the argument vector parsed from the string. This vector
2327 could be modified (e.g. insert additional args into it) before
2328 passing it to the next function that actually executes it. Though
2329 it still isn't perfect for chain actions.. will reconsider & re-
2330 factor when I have the time. -duc */
2332 char **parts = g_strsplit(act, " ", 2);
2334 if (g_strcmp0(parts[0], "chain") == 0) {
2335 GString *newargs = g_string_new("");
2336 gchar **chainparts = split_quoted(parts[1], FALSE);
2338 /* for every argument in the chain, inject the handler args
2339 and make sure the new parts are wrapped in quotes */
2340 gchar **cp = chainparts;
2342 gchar *quotless = NULL;
2343 gchar **spliced_quotless = NULL; // sigh -_-;
2344 gchar **inpart = NULL;
2347 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2349 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2350 } else quotless = g_strdup(*cp);
2352 spliced_quotless = g_strsplit(quotless, " ", 2);
2353 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2354 g_strfreev(spliced_quotless);
2356 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2362 parse_command(parts[0], &(newargs->str[1]), NULL);
2363 g_string_free(newargs, TRUE);
2364 g_strfreev(chainparts);
2367 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2368 parse_command(inparts[0], inparts[1], NULL);
2376 add_binding (const gchar *key, const gchar *act) {
2377 char **parts = g_strsplit(act, " ", 2);
2384 if (uzbl.state.verbose)
2385 printf ("Binding %-10s : %s\n", key, act);
2386 action = new_action(parts[0], parts[1]);
2388 if (g_hash_table_remove (uzbl.bindings, key))
2389 g_warning ("Overwriting existing binding for \"%s\"", key);
2390 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2395 get_xdg_var (XDG_Var xdg) {
2396 const gchar* actual_value = getenv (xdg.environmental);
2397 const gchar* home = getenv ("HOME");
2398 gchar* return_value;
2400 if (! actual_value || strcmp (actual_value, "") == 0) {
2401 if (xdg.default_value) {
2402 return_value = str_replace ("~", home, xdg.default_value);
2404 return_value = NULL;
2407 return_value = str_replace("~", home, actual_value);
2410 return return_value;
2414 find_xdg_file (int xdg_type, char* filename) {
2415 /* xdg_type = 0 => config
2416 xdg_type = 1 => data
2417 xdg_type = 2 => cache*/
2419 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2420 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2423 gchar* temporary_string;
2427 if (! file_exists (temporary_file) && xdg_type != 2) {
2428 buf = get_xdg_var (XDG[3 + xdg_type]);
2429 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2432 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2433 g_free (temporary_file);
2434 temporary_file = g_strconcat (temporary_string, filename, NULL);
2438 //g_free (temporary_string); - segfaults.
2440 if (file_exists (temporary_file)) {
2441 return temporary_file;
2448 State *s = &uzbl.state;
2449 Network *n = &uzbl.net;
2451 for (i = 0; default_config[i].command != NULL; i++) {
2452 parse_cmd_line(default_config[i].command, NULL);
2455 if (g_strcmp0(s->config_file, "-") == 0) {
2456 s->config_file = NULL;
2460 else if (!s->config_file) {
2461 s->config_file = find_xdg_file (0, "/uzbl/config");
2464 if (s->config_file) {
2465 GArray* lines = read_file_by_line (s->config_file);
2469 while ((line = g_array_index(lines, gchar*, i))) {
2470 parse_cmd_line (line, NULL);
2474 g_array_free (lines, TRUE);
2476 if (uzbl.state.verbose)
2477 printf ("No configuration file loaded.\n");
2480 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2483 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2486 if (!uzbl.behave.cookie_handler)
2489 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2490 GString *s = g_string_new ("");
2491 SoupURI * soup_uri = soup_message_get_uri(msg);
2492 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2493 run_handler(uzbl.behave.cookie_handler, s->str);
2495 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2496 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2497 if ( p != NULL ) *p = '\0';
2498 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2500 if (uzbl.comm.sync_stdout)
2501 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2503 g_string_free(s, TRUE);
2507 save_cookies (SoupMessage *msg, gpointer user_data){
2511 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2512 cookie = soup_cookie_to_set_cookie_header(ck->data);
2513 SoupURI * soup_uri = soup_message_get_uri(msg);
2514 GString *s = g_string_new ("");
2515 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2516 run_handler(uzbl.behave.cookie_handler, s->str);
2518 g_string_free(s, TRUE);
2523 /* --- WEBINSPECTOR --- */
2525 hide_window_cb(GtkWidget *widget, gpointer data) {
2528 gtk_widget_hide(widget);
2531 static WebKitWebView*
2532 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2535 (void) web_inspector;
2536 GtkWidget* scrolled_window;
2537 GtkWidget* new_web_view;
2540 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2541 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2542 G_CALLBACK(hide_window_cb), NULL);
2544 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2545 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2546 gtk_widget_show(g->inspector_window);
2548 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2549 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2550 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2551 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2552 gtk_widget_show(scrolled_window);
2554 new_web_view = webkit_web_view_new();
2555 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2557 return WEBKIT_WEB_VIEW(new_web_view);
2561 inspector_show_window_cb (WebKitWebInspector* inspector){
2563 gtk_widget_show(uzbl.gui.inspector_window);
2567 /* TODO: Add variables and code to make use of these functions */
2569 inspector_close_window_cb (WebKitWebInspector* inspector){
2575 inspector_attach_window_cb (WebKitWebInspector* inspector){
2581 inspector_detach_window_cb (WebKitWebInspector* inspector){
2587 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2593 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2599 set_up_inspector() {
2601 WebKitWebSettings *settings = view_settings();
2602 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2604 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2605 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2606 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2607 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2608 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2609 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2610 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2612 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2616 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2618 uzbl_cmdprop *c = v;
2623 if(c->type == TYPE_STR)
2624 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2625 else if(c->type == TYPE_INT)
2626 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2630 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2634 printf("bind %s = %s %s\n", (char *)k ,
2635 (char *)a->name, a->param?(char *)a->param:"");
2640 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2641 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2644 /* set up gtk, gobject, variable defaults and other things that tests and other
2645 * external applications need to do anyhow */
2647 initialize(int argc, char *argv[]) {
2648 gtk_init (&argc, &argv);
2649 if (!g_thread_supported ())
2650 g_thread_init (NULL);
2651 uzbl.state.executable_path = g_strdup(argv[0]);
2652 uzbl.state.selected_url = NULL;
2653 uzbl.state.searchtx = NULL;
2655 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2656 g_option_context_add_main_entries (context, entries, NULL);
2657 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2658 g_option_context_parse (context, &argc, &argv, NULL);
2659 g_option_context_free(context);
2661 /* initialize hash table */
2662 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2664 uzbl.net.soup_session = webkit_get_default_session();
2665 uzbl.state.keycmd = g_string_new("");
2667 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2668 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2669 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2670 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2671 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2672 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2674 uzbl.gui.sbar.progress_s = g_strdup("=");
2675 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2676 uzbl.gui.sbar.progress_w = 10;
2678 /* HTML mode defaults*/
2679 uzbl.behave.html_buffer = g_string_new("");
2680 uzbl.behave.html_endmarker = g_strdup(".");
2681 uzbl.behave.html_timeout = 60;
2682 uzbl.behave.base_url = g_strdup("http://invalid");
2684 /* default mode indicators */
2685 uzbl.behave.insert_indicator = g_strdup("I");
2686 uzbl.behave.cmd_indicator = g_strdup("C");
2688 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2689 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2690 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2691 uzbl.info.arch = ARCH;
2692 uzbl.info.commit = COMMIT;
2696 make_var_to_name_hash();
2698 uzbl.gui.scrolled_win = create_browser();
2701 #ifndef UZBL_LIBRARY
2704 main (int argc, char* argv[]) {
2705 initialize(argc, argv);
2707 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2711 /* initial packing */
2712 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2713 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2715 if (uzbl.state.socket_id) {
2716 uzbl.gui.plug = create_plug ();
2717 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2718 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2720 uzbl.gui.main_window = create_window ();
2721 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2722 gtk_widget_show_all (uzbl.gui.main_window);
2723 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2726 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2728 if (uzbl.state.verbose) {
2729 printf("Uzbl start location: %s\n", argv[0]);
2730 if (uzbl.state.socket_id)
2731 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2733 printf("window_id %i\n",(int) uzbl.xwin);
2734 printf("pid %i\n", getpid ());
2735 printf("name: %s\n", uzbl.state.instance_name);
2738 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2739 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2740 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2741 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2742 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2746 if (!uzbl.behave.show_status)
2747 gtk_widget_hide(uzbl.gui.mainbar);
2754 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2755 gboolean verbose_override = uzbl.state.verbose;
2757 if (verbose_override > uzbl.state.verbose)
2758 uzbl.state.verbose = verbose_override;
2761 set_var_value("uri", uri_override);
2762 g_free(uri_override);
2763 } else if (uzbl.state.uri)
2764 cmd_load_uri(uzbl.gui.web_view, NULL);
2769 return EXIT_SUCCESS;
2773 /* vi: set et ts=4: */