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>
62 /* commandline arguments (set initial values for the state variables) */
64 GOptionEntry entries[] =
66 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
67 "Uri to load at startup (equivalent to 'uzbl <uri>' or 'set uri = URI' after uzbl has launched)", "URI" },
68 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
69 "Whether to print all messages or just errors.", NULL },
70 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
71 "Name of the current instance (defaults to Xorg window id)", "NAME" },
72 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
73 "Path to config file or '-' for stdin", "FILE" },
74 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
75 "Socket ID", "SOCKET" },
76 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
77 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
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 */
85 /* TODO: Make this ambiguous void **ptr into a union { char *char_p; int *int_p; float *float_p; } val;
86 the PTR() macro is kind of preventing this change at the moment. */
94 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
96 /* abbreviations to help keep the table's width humane */
97 #define PTR_V(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .writeable = 1, .func = fun }
98 #define PTR_C(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = 0, .writeable = 0, .func = fun }
103 } var_name_to_ptr[] = {
104 /* variable name pointer to variable in code type dump callback function */
105 /* ---------------------------------------------------------------------------------------------- */
106 { "uri", PTR_V(uzbl.state.uri, STR, 1, cmd_load_uri)},
107 { "verbose", PTR_V(uzbl.state.verbose, INT, 1, NULL)},
108 { "mode", PTR_V(uzbl.behave.mode, INT, 0, NULL)},
109 { "inject_html", PTR_V(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
110 { "base_url", PTR_V(uzbl.behave.base_url, STR, 1, NULL)},
111 { "html_endmarker", PTR_V(uzbl.behave.html_endmarker, STR, 1, NULL)},
112 { "html_mode_timeout", PTR_V(uzbl.behave.html_timeout, INT, 1, NULL)},
113 { "keycmd", PTR_V(uzbl.state.keycmd, STR, 1, set_keycmd)},
114 { "status_message", PTR_V(uzbl.gui.sbar.msg, STR, 1, update_title)},
115 { "show_status", PTR_V(uzbl.behave.show_status, INT, 1, cmd_set_status)},
116 { "status_top", PTR_V(uzbl.behave.status_top, INT, 1, move_statusbar)},
117 { "status_format", PTR_V(uzbl.behave.status_format, STR, 1, update_title)},
118 { "status_pbar_done", PTR_V(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
119 { "status_pbar_pending", PTR_V(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
120 { "status_pbar_width", PTR_V(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
121 { "status_background", PTR_V(uzbl.behave.status_background, STR, 1, update_title)},
122 { "insert_indicator", PTR_V(uzbl.behave.insert_indicator, STR, 1, update_indicator)},
123 { "command_indicator", PTR_V(uzbl.behave.cmd_indicator, STR, 1, update_indicator)},
124 { "title_format_long", PTR_V(uzbl.behave.title_format_long, STR, 1, update_title)},
125 { "title_format_short", PTR_V(uzbl.behave.title_format_short, STR, 1, update_title)},
126 { "icon", PTR_V(uzbl.gui.icon, STR, 1, set_icon)},
127 { "insert_mode", PTR_V(uzbl.behave.insert_mode, INT, 1, set_mode_indicator)},
128 { "always_insert_mode", PTR_V(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
129 { "reset_command_mode", PTR_V(uzbl.behave.reset_command_mode, INT, 1, NULL)},
130 { "modkey", PTR_V(uzbl.behave.modkey, STR, 1, cmd_modkey)},
131 { "load_finish_handler", PTR_V(uzbl.behave.load_finish_handler, STR, 1, NULL)},
132 { "load_start_handler", PTR_V(uzbl.behave.load_start_handler, STR, 1, NULL)},
133 { "load_commit_handler", PTR_V(uzbl.behave.load_commit_handler, STR, 1, NULL)},
134 { "history_handler", PTR_V(uzbl.behave.history_handler, STR, 1, NULL)},
135 { "download_handler", PTR_V(uzbl.behave.download_handler, STR, 1, NULL)},
136 { "cookie_handler", PTR_V(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
137 { "new_window", PTR_V(uzbl.behave.new_window, STR, 1, cmd_new_window)},
138 { "fifo_dir", PTR_V(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
139 { "socket_dir", PTR_V(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
140 { "http_debug", PTR_V(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
141 { "shell_cmd", PTR_V(uzbl.behave.shell_cmd, STR, 1, NULL)},
142 { "proxy_url", PTR_V(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
143 { "max_conns", PTR_V(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
144 { "max_conns_host", PTR_V(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
145 { "useragent", PTR_V(uzbl.net.useragent, STR, 1, cmd_useragent)},
147 /* exported WebKitWebSettings properties */
148 { "zoom_level", PTR_V(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
149 { "font_size", PTR_V(uzbl.behave.font_size, INT, 1, cmd_font_size)},
150 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
151 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
152 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
153 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
154 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
155 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
156 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
157 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
158 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
159 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
160 { "resizable_text_areas",PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
161 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
162 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
163 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
165 /* constants (not dumpable or writeable) */
166 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
167 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
168 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
169 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
170 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
171 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
172 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
173 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
174 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
175 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
176 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
178 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
179 }, *n2v_p = var_name_to_ptr;
186 { "SHIFT", GDK_SHIFT_MASK }, // shift
187 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
188 { "CONTROL", GDK_CONTROL_MASK }, // control
189 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
190 { "MOD2", GDK_MOD2_MASK }, // 5th mod
191 { "MOD3", GDK_MOD3_MASK }, // 6th mod
192 { "MOD4", GDK_MOD4_MASK }, // 7th mod
193 { "MOD5", GDK_MOD5_MASK }, // 8th mod
194 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
195 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
196 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
197 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
198 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
199 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
200 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
201 { "META", GDK_META_MASK }, // meta (since 2.10)
206 /* construct a hash from the var_name_to_ptr array for quick access */
208 make_var_to_name_hash() {
209 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
211 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
216 /* --- UTILITY FUNCTIONS --- */
217 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
219 get_exp_type(gchar *s) {
223 else if(*(s+1) == '{')
224 return EXP_BRACED_VAR;
225 else if(*(s+1) == '<')
227 else if(*(s+1) == '[')
230 return EXP_SIMPLE_VAR;
236 * recurse == 1: don't expand '@(command)@'
237 * recurse == 2: don't expand '@<java script>@'
240 expand(char *s, guint recurse) {
244 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
249 gchar *cmd_stdout = NULL;
251 GString *buf = g_string_new("");
252 GString *js_ret = g_string_new("");
257 g_string_append_c(buf, *++s);
262 etype = get_exp_type(s);
267 vend = strpbrk(s, end_simple_var);
268 if(!vend) vend = strchr(s, '\0');
272 vend = strchr(s, upto);
273 if(!vend) vend = strchr(s, '\0');
277 strcpy(str_end, ")@");
279 vend = strstr(s, str_end);
280 if(!vend) vend = strchr(s, '\0');
284 strcpy(str_end, ">@");
286 vend = strstr(s, str_end);
287 if(!vend) vend = strchr(s, '\0');
291 strcpy(str_end, "]@");
293 vend = strstr(s, str_end);
294 if(!vend) vend = strchr(s, '\0');
299 strncpy(ret, s, vend-s);
303 if(etype == EXP_SIMPLE_VAR ||
304 etype == EXP_BRACED_VAR) {
305 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
306 //printf("expand() RET: %s\n", ret);
307 if(c->type == TYPE_STR && *c->ptr != NULL) {
308 //printf("expand() buf: %s\n\n", (char *)*c->ptr);
309 gchar *tmp = g_strdup((char *)*c->ptr);
310 g_string_append(buf, tmp);
311 } else if(c->type == TYPE_INT) {
312 g_string_append_printf(buf, "%d", (int)*c->ptr);
314 else if(c->type == TYPE_FLOAT) {
315 g_string_append_printf(buf, "%f", *(float *)c->ptr);
319 if(etype == EXP_SIMPLE_VAR)
324 else if(recurse != 1 &&
326 mycmd = expand(ret, 1);
327 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
331 g_printerr("error on running command: %s\n", err->message);
334 else if (*cmd_stdout) {
335 int len = strlen(cmd_stdout);
337 if(cmd_stdout[len-1] == '\n')
338 cmd_stdout[--len] = 0; /* strip trailing newline */
340 g_string_append(buf, cmd_stdout);
345 else if(recurse != 2 &&
347 mycmd = expand(ret, 2);
348 eval_js(uzbl.gui.web_view, mycmd, js_ret);
352 g_string_append(buf, js_ret->str);
353 g_string_free(js_ret, TRUE);
354 js_ret = g_string_new("");
358 else if(etype == EXP_ESCAPE) {
359 mycmd = expand(ret, 0);
360 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
362 g_string_append(buf, escaped);
371 g_string_append_c(buf, *s);
376 g_string_free(js_ret, TRUE);
377 return g_string_free(buf, FALSE);
384 snprintf(tmp, sizeof(tmp), "%i", val);
385 return g_strdup(tmp);
389 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
392 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
395 str_replace (const char* search, const char* replace, const char* string) {
399 buf = g_strsplit (string, search, -1);
400 ret = g_strjoinv (replace, buf);
401 g_strfreev(buf); // somebody said this segfaults
407 read_file_by_line (gchar *path) {
408 GIOChannel *chan = NULL;
409 gchar *readbuf = NULL;
411 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
414 chan = g_io_channel_new_file(path, "r", NULL);
417 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
418 const gchar* val = g_strdup (readbuf);
419 g_array_append_val (lines, val);
424 g_io_channel_unref (chan);
426 fprintf(stderr, "File '%s' not be read.\n", path);
433 parseenv (char* string) {
434 extern char** environ;
435 gchar* tmpstr = NULL;
439 while (environ[i] != NULL) {
440 gchar** env = g_strsplit (environ[i], "=", 2);
441 gchar* envname = g_strconcat ("$", env[0], NULL);
443 if (g_strrstr (string, envname) != NULL) {
444 tmpstr = g_strdup(string);
446 string = str_replace(envname, env[1], tmpstr);
451 g_strfreev (env); // somebody said this breaks uzbl
459 setup_signal(int signr, sigfunc *shandler) {
460 struct sigaction nh, oh;
462 nh.sa_handler = shandler;
463 sigemptyset(&nh.sa_mask);
466 if(sigaction(signr, &nh, &oh) < 0)
474 if (uzbl.behave.fifo_dir)
475 unlink (uzbl.comm.fifo_path);
476 if (uzbl.behave.socket_dir)
477 unlink (uzbl.comm.socket_path);
479 g_free(uzbl.state.executable_path);
480 g_free(uzbl.state.keycmd);
481 g_hash_table_destroy(uzbl.bindings);
482 g_hash_table_destroy(uzbl.behave.commands);
485 /* used for html_mode_timeout
486 * be sure to extend this function to use
487 * more timers if needed in other places
490 set_timeout(int seconds) {
492 memset(&t, 0, sizeof t);
494 t.it_value.tv_sec = seconds;
495 t.it_value.tv_usec = 0;
496 setitimer(ITIMER_REAL, &t, NULL);
499 /* --- SIGNAL HANDLER --- */
502 catch_sigterm(int s) {
508 catch_sigint(int s) {
518 set_var_value("mode", "0");
523 /* --- CALLBACKS --- */
526 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
529 (void) navigation_action;
530 (void) policy_decision;
532 const gchar* uri = webkit_network_request_get_uri (request);
533 if (uzbl.state.verbose)
534 printf("New window requested -> %s \n", uri);
535 webkit_web_policy_decision_use(policy_decision);
540 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
545 /* If we can display it, let's display it... */
546 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
547 webkit_web_policy_decision_use (policy_decision);
551 /* ...everything we can't displayed is downloaded */
552 webkit_web_policy_decision_download (policy_decision);
557 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
561 if (uzbl.state.selected_url != NULL) {
562 if (uzbl.state.verbose)
563 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
564 new_window_load_uri(uzbl.state.selected_url);
566 if (uzbl.state.verbose)
567 printf("New web view -> %s\n","Nothing to open, exiting");
573 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
576 if (uzbl.behave.download_handler) {
577 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
578 if (uzbl.state.verbose)
579 printf("Download -> %s\n",uri);
580 /* if urls not escaped, we may have to escape and quote uri before this call */
581 run_handler(uzbl.behave.download_handler, uri);
586 /* scroll a bar in a given direction */
588 scroll (GtkAdjustment* bar, GArray *argv) {
592 gdouble page_size = gtk_adjustment_get_page_size(bar);
593 gdouble value = gtk_adjustment_get_value(bar);
594 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
597 value += page_size * amount * 0.01;
601 max_value = gtk_adjustment_get_upper(bar) - page_size;
603 if (value > max_value)
604 value = max_value; /* don't scroll past the end of the page */
606 gtk_adjustment_set_value (bar, value);
610 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
611 (void) page; (void) argv; (void) result;
612 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
616 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
617 (void) page; (void) argv; (void) result;
618 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
619 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
623 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
624 (void) page; (void) result;
625 scroll(uzbl.gui.bar_v, argv);
629 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
630 (void) page; (void) result;
631 scroll(uzbl.gui.bar_h, argv);
636 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
637 if(uzbl.state.verbose)
638 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
640 /* update geometry var with the actual geometry
641 this is necessary as some WMs don't seem to honour
642 the above setting and we don't want to end up with
643 wrong geometry information
650 if (!uzbl.behave.show_status) {
651 gtk_widget_hide(uzbl.gui.mainbar);
653 gtk_widget_show(uzbl.gui.mainbar);
659 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
664 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
668 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
673 if (uzbl.behave.show_status) {
674 gtk_widget_hide(uzbl.gui.mainbar);
676 gtk_widget_show(uzbl.gui.mainbar);
678 uzbl.behave.show_status = !uzbl.behave.show_status;
683 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
687 //Set selected_url state variable
688 g_free(uzbl.state.selected_url);
689 uzbl.state.selected_url = NULL;
691 uzbl.state.selected_url = g_strdup(link);
697 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
700 const gchar *title = webkit_web_view_get_title(web_view);
701 if (uzbl.gui.main_title)
702 g_free (uzbl.gui.main_title);
703 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
708 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
711 uzbl.gui.sbar.load_progress = progress;
713 g_free(uzbl.gui.sbar.progress_bar);
714 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
720 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
724 if (uzbl.behave.load_finish_handler)
725 run_handler(uzbl.behave.load_finish_handler, "");
728 void clear_keycmd() {
729 g_free(uzbl.state.keycmd);
730 uzbl.state.keycmd = g_strdup("");
734 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
738 uzbl.gui.sbar.load_progress = 0;
739 clear_keycmd(); // don't need old commands to remain on new page?
740 if (uzbl.behave.load_start_handler)
741 run_handler(uzbl.behave.load_start_handler, "");
745 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
748 g_free (uzbl.state.uri);
749 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
750 uzbl.state.uri = g_string_free (newuri, FALSE);
751 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
752 set_insert_mode(uzbl.behave.always_insert_mode);
755 if (uzbl.behave.load_commit_handler)
756 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
760 destroy_cb (GtkWidget* widget, gpointer data) {
768 if (uzbl.behave.history_handler) {
770 struct tm * timeinfo;
773 timeinfo = localtime ( &rawtime );
774 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
775 run_handler(uzbl.behave.history_handler, date);
780 /* VIEW funcs (little webkit wrappers) */
781 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
783 VIEWFUNC(reload_bypass_cache)
784 VIEWFUNC(stop_loading)
791 /* -- command to callback/function map for things we cannot attach to any signals */
792 struct {char *key; CommandInfo value;} cmdlist[] =
793 { /* key function no_split */
794 { "back", {view_go_back, 0} },
795 { "forward", {view_go_forward, 0} },
796 { "scroll_vert", {scroll_vert, 0} },
797 { "scroll_horz", {scroll_horz, 0} },
798 { "scroll_begin", {scroll_begin, 0} },
799 { "scroll_end", {scroll_end, 0} },
800 { "reload", {view_reload, 0}, },
801 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
802 { "stop", {view_stop_loading, 0}, },
803 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
804 { "zoom_out", {view_zoom_out, 0}, },
805 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
806 { "uri", {load_uri, TRUE} },
807 { "js", {run_js, TRUE} },
808 { "script", {run_external_js, 0} },
809 { "toggle_status", {toggle_status_cb, 0} },
810 { "spawn", {spawn, 0} },
811 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
812 { "sh", {spawn_sh, 0} },
813 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
814 { "exit", {close_uzbl, 0} },
815 { "search", {search_forward_text, TRUE} },
816 { "search_reverse", {search_reverse_text, TRUE} },
817 { "dehilight", {dehilight, 0} },
818 { "toggle_insert_mode", {toggle_insert_mode, 0} },
819 { "set", {set_var, TRUE} },
820 //{ "get", {get_var, TRUE} },
821 { "bind", {act_bind, TRUE} },
822 { "dump_config", {act_dump_config, 0} },
823 { "keycmd", {keycmd, TRUE} },
824 { "keycmd_nl", {keycmd_nl, TRUE} },
825 { "keycmd_bs", {keycmd_bs, 0} },
826 { "chain", {chain, 0} },
827 { "print", {print, TRUE} }
834 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
836 for (i = 0; i < LENGTH(cmdlist); i++)
837 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
840 /* -- CORE FUNCTIONS -- */
843 free_action(gpointer act) {
844 Action *action = (Action*)act;
845 g_free(action->name);
847 g_free(action->param);
852 new_action(const gchar *name, const gchar *param) {
853 Action *action = g_new(Action, 1);
855 action->name = g_strdup(name);
857 action->param = g_strdup(param);
859 action->param = NULL;
865 file_exists (const char * filename) {
866 return (access(filename, F_OK) == 0);
870 set_var(WebKitWebView *page, GArray *argv, GString *result) {
871 (void) page; (void) result;
872 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
873 if (split[0] != NULL) {
874 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
875 set_var_value(g_strstrip(split[0]), value);
882 print(WebKitWebView *page, GArray *argv, GString *result) {
883 (void) page; (void) result;
886 buf = expand(argv_idx(argv, 0), 0);
887 g_string_assign(result, buf);
892 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
893 (void) page; (void) result;
894 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
895 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
896 add_binding(g_strstrip(split[0]), value);
914 set_mode_indicator() {
915 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
916 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
921 set_mode_indicator();
926 set_insert_mode(gboolean mode) {
927 uzbl.behave.insert_mode = mode;
928 set_mode_indicator();
932 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
933 (void) page; (void) result;
935 if (argv_idx(argv, 0)) {
936 if (strcmp (argv_idx(argv, 0), "0") == 0) {
937 set_insert_mode(FALSE);
939 set_insert_mode(TRUE);
942 set_insert_mode( !uzbl.behave.insert_mode );
949 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
952 if (argv_idx(argv, 0)) {
953 GString* newuri = g_string_new (argv_idx(argv, 0));
954 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
955 run_js(web_view, argv, NULL);
958 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
959 g_string_prepend (newuri, "http://");
960 /* if we do handle cookies, ask our handler for them */
961 webkit_web_view_load_uri (web_view, newuri->str);
962 g_string_free (newuri, TRUE);
969 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
970 size_t argumentCount, const JSValueRef arguments[],
971 JSValueRef* exception) {
976 JSStringRef js_result_string;
977 GString *result = g_string_new("");
979 if (argumentCount >= 1) {
980 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
981 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
982 char ctl_line[arg_size];
983 JSStringGetUTF8CString(arg, ctl_line, arg_size);
985 parse_cmd_line(ctl_line, result);
987 JSStringRelease(arg);
989 js_result_string = JSStringCreateWithUTF8CString(result->str);
991 g_string_free(result, TRUE);
993 return JSValueMakeString(ctx, js_result_string);
996 JSStaticFunction js_static_functions[] = {
997 {"run", js_run_command, kJSPropertyAttributeNone},
1002 /* This function creates the class and its definition, only once */
1003 if (!uzbl.js.initialized) {
1004 /* it would be pretty cool to make this dynamic */
1005 uzbl.js.classdef = kJSClassDefinitionEmpty;
1006 uzbl.js.classdef.staticFunctions = js_static_functions;
1008 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1014 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1015 WebKitWebFrame *frame;
1016 JSGlobalContextRef context;
1017 JSObjectRef globalobject;
1018 JSStringRef var_name;
1020 JSStringRef js_script;
1021 JSValueRef js_result;
1022 JSStringRef js_result_string;
1023 size_t js_result_size;
1027 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1028 context = webkit_web_frame_get_global_context(frame);
1029 globalobject = JSContextGetGlobalObject(context);
1031 /* uzbl javascript namespace */
1032 var_name = JSStringCreateWithUTF8CString("Uzbl");
1033 JSObjectSetProperty(context, globalobject, var_name,
1034 JSObjectMake(context, uzbl.js.classref, NULL),
1035 kJSClassAttributeNone, NULL);
1037 /* evaluate the script and get return value*/
1038 js_script = JSStringCreateWithUTF8CString(script);
1039 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1040 if (js_result && !JSValueIsUndefined(context, js_result)) {
1041 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1042 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1044 if (js_result_size) {
1045 char js_result_utf8[js_result_size];
1046 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1047 g_string_assign(result, js_result_utf8);
1050 JSStringRelease(js_result_string);
1054 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1056 JSStringRelease(var_name);
1057 JSStringRelease(js_script);
1061 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1062 if (argv_idx(argv, 0))
1063 eval_js(web_view, argv_idx(argv, 0), result);
1067 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1069 if (argv_idx(argv, 0)) {
1070 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1075 while ((line = g_array_index(lines, gchar*, i))) {
1077 js = g_strdup (line);
1079 gchar* newjs = g_strconcat (js, line, NULL);
1086 if (uzbl.state.verbose)
1087 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1089 if (argv_idx (argv, 1)) {
1090 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1094 eval_js (web_view, js, result);
1096 g_array_free (lines, TRUE);
1101 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1102 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1103 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1104 webkit_web_view_unmark_text_matches (page);
1105 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1106 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1110 if (uzbl.state.searchtx) {
1111 if (uzbl.state.verbose)
1112 printf ("Searching: %s\n", uzbl.state.searchtx);
1113 webkit_web_view_set_highlight_text_matches (page, TRUE);
1114 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1119 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1121 search_text(page, argv, TRUE);
1125 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1127 search_text(page, argv, FALSE);
1131 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1132 (void) argv; (void) result;
1133 webkit_web_view_set_highlight_text_matches (page, FALSE);
1138 new_window_load_uri (const gchar * uri) {
1139 if (uzbl.behave.new_window) {
1140 GString *s = g_string_new ("");
1141 g_string_printf(s, "'%s'", uri);
1142 run_handler(uzbl.behave.new_window, s->str);
1145 GString* to_execute = g_string_new ("");
1146 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1148 for (i = 0; entries[i].long_name != NULL; i++) {
1149 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1150 gchar** str = (gchar**)entries[i].arg_data;
1152 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1156 if (uzbl.state.verbose)
1157 printf("\n%s\n", to_execute->str);
1158 g_spawn_command_line_async (to_execute->str, NULL);
1159 g_string_free (to_execute, TRUE);
1163 chain (WebKitWebView *page, GArray *argv, GString *result) {
1164 (void) page; (void) result;
1166 gchar **parts = NULL;
1168 while ((a = argv_idx(argv, i++))) {
1169 parts = g_strsplit (a, " ", 2);
1171 parse_command(parts[0], parts[1], result);
1177 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1181 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1187 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1191 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1197 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1202 int len = strlen(uzbl.state.keycmd);
1203 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1205 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1210 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1217 /* --Statusbar functions-- */
1219 build_progressbar_ascii(int percent) {
1220 int width=uzbl.gui.sbar.progress_w;
1223 GString *bar = g_string_new("");
1225 l = (double)percent*((double)width/100.);
1226 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1228 for(i=0; i<(int)l; i++)
1229 g_string_append(bar, uzbl.gui.sbar.progress_s);
1232 g_string_append(bar, uzbl.gui.sbar.progress_u);
1234 return g_string_free(bar, FALSE);
1236 /* --End Statusbar functions-- */
1239 sharg_append(GArray *a, const gchar *str) {
1240 const gchar *s = (str ? str : "");
1241 g_array_append_val(a, s);
1244 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1246 run_command (const gchar *command, const guint npre, const gchar **args,
1247 const gboolean sync, char **output_stdout) {
1248 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1251 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1252 gchar *pid = itos(getpid());
1253 gchar *xwin = itos(uzbl.xwin);
1255 sharg_append(a, command);
1256 for (i = 0; i < npre; i++) /* add n args before the default vars */
1257 sharg_append(a, args[i]);
1258 sharg_append(a, uzbl.state.config_file);
1259 sharg_append(a, pid);
1260 sharg_append(a, xwin);
1261 sharg_append(a, uzbl.comm.fifo_path);
1262 sharg_append(a, uzbl.comm.socket_path);
1263 sharg_append(a, uzbl.state.uri);
1264 sharg_append(a, uzbl.gui.main_title);
1266 for (i = npre; i < g_strv_length((gchar**)args); i++)
1267 sharg_append(a, args[i]);
1271 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1273 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1274 NULL, NULL, output_stdout, NULL, NULL, &err);
1275 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1276 NULL, NULL, NULL, &err);
1278 if (uzbl.state.verbose) {
1279 GString *s = g_string_new("spawned:");
1280 for (i = 0; i < (a->len); i++) {
1281 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1282 g_string_append_printf(s, " %s", qarg);
1285 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1286 printf("%s\n", s->str);
1287 g_string_free(s, TRUE);
1289 printf("Stdout: %s\n", *output_stdout);
1293 g_printerr("error on run_command: %s\n", err->message);
1298 g_array_free (a, TRUE);
1303 split_quoted(const gchar* src, const gboolean unquote) {
1304 /* split on unquoted space, return array of strings;
1305 remove a layer of quotes and backslashes if unquote */
1306 if (!src) return NULL;
1308 gboolean dq = FALSE;
1309 gboolean sq = FALSE;
1310 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1311 GString *s = g_string_new ("");
1315 for (p = src; *p != '\0'; p++) {
1316 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1317 else if (*p == '\\') { g_string_append_c(s, *p++);
1318 g_string_append_c(s, *p); }
1319 else if ((*p == '"') && unquote && !sq) dq = !dq;
1320 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1322 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1323 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1325 else if ((*p == ' ') && !dq && !sq) {
1326 dup = g_strdup(s->str);
1327 g_array_append_val(a, dup);
1328 g_string_truncate(s, 0);
1329 } else g_string_append_c(s, *p);
1331 dup = g_strdup(s->str);
1332 g_array_append_val(a, dup);
1333 ret = (gchar**)a->data;
1334 g_array_free (a, FALSE);
1335 g_string_free (s, TRUE);
1340 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1341 (void)web_view; (void)result;
1342 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1343 if (argv_idx(argv, 0))
1344 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1348 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1349 (void)web_view; (void)result;
1351 if (argv_idx(argv, 0))
1352 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1353 TRUE, &uzbl.comm.sync_stdout);
1357 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1358 (void)web_view; (void)result;
1359 if (!uzbl.behave.shell_cmd) {
1360 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1365 gchar *spacer = g_strdup("");
1366 g_array_insert_val(argv, 1, spacer);
1367 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1369 for (i = 1; i < g_strv_length(cmd); i++)
1370 g_array_prepend_val(argv, cmd[i]);
1372 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1378 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1379 (void)web_view; (void)result;
1380 if (!uzbl.behave.shell_cmd) {
1381 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1386 gchar *spacer = g_strdup("");
1387 g_array_insert_val(argv, 1, spacer);
1388 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1390 for (i = 1; i < g_strv_length(cmd); i++)
1391 g_array_prepend_val(argv, cmd[i]);
1393 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1394 TRUE, &uzbl.comm.sync_stdout);
1400 parse_command(const char *cmd, const char *param, GString *result) {
1403 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1405 gchar **par = split_quoted(param, TRUE);
1406 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1408 if (c->no_split) { /* don't split */
1409 sharg_append(a, param);
1411 for (i = 0; i < g_strv_length(par); i++)
1412 sharg_append(a, par[i]);
1415 if (result == NULL) {
1416 GString *result_print = g_string_new("");
1418 c->function(uzbl.gui.web_view, a, result_print);
1419 if (result_print->len)
1420 printf("%*s\n", result_print->len, result_print->str);
1422 g_string_free(result_print, TRUE);
1424 c->function(uzbl.gui.web_view, a, result);
1427 g_array_free (a, TRUE);
1430 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1437 if(*uzbl.net.proxy_url == ' '
1438 || uzbl.net.proxy_url == NULL) {
1439 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1440 (GType) SOUP_SESSION_PROXY_URI);
1443 suri = soup_uri_new(uzbl.net.proxy_url);
1444 g_object_set(G_OBJECT(uzbl.net.soup_session),
1445 SOUP_SESSION_PROXY_URI,
1447 soup_uri_free(suri);
1454 if(file_exists(uzbl.gui.icon)) {
1455 if (uzbl.gui.main_window)
1456 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1458 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1464 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1465 g_array_append_val (a, uzbl.state.uri);
1466 load_uri(uzbl.gui.web_view, a, NULL);
1467 g_array_free (a, TRUE);
1471 cmd_always_insert_mode() {
1472 set_insert_mode(uzbl.behave.always_insert_mode);
1478 g_object_set(G_OBJECT(uzbl.net.soup_session),
1479 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1483 cmd_max_conns_host() {
1484 g_object_set(G_OBJECT(uzbl.net.soup_session),
1485 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1490 soup_session_remove_feature
1491 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1492 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1493 /*g_free(uzbl.net.soup_logger);*/
1495 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1496 soup_session_add_feature(uzbl.net.soup_session,
1497 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1502 return webkit_web_view_get_settings(uzbl.gui.web_view);
1507 WebKitWebSettings *ws = view_settings();
1508 if (uzbl.behave.font_size > 0) {
1509 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1512 if (uzbl.behave.monospace_size > 0) {
1513 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1514 uzbl.behave.monospace_size, NULL);
1516 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1517 uzbl.behave.font_size, NULL);
1523 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1527 cmd_disable_plugins() {
1528 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1529 !uzbl.behave.disable_plugins, NULL);
1533 cmd_disable_scripts() {
1534 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1535 !uzbl.behave.disable_scripts, NULL);
1539 cmd_minimum_font_size() {
1540 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1541 uzbl.behave.minimum_font_size, NULL);
1544 cmd_autoload_img() {
1545 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1546 uzbl.behave.autoload_img, NULL);
1551 cmd_autoshrink_img() {
1552 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1553 uzbl.behave.autoshrink_img, NULL);
1558 cmd_enable_spellcheck() {
1559 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1560 uzbl.behave.enable_spellcheck, NULL);
1564 cmd_enable_private() {
1565 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1566 uzbl.behave.enable_private, NULL);
1571 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1572 uzbl.behave.print_bg, NULL);
1577 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1578 uzbl.behave.style_uri, NULL);
1582 cmd_resizable_txt() {
1583 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1584 uzbl.behave.resizable_txt, NULL);
1588 cmd_default_encoding() {
1589 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1590 uzbl.behave.default_encoding, NULL);
1594 cmd_enforce_96dpi() {
1595 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1596 uzbl.behave.enforce_96dpi, NULL);
1600 cmd_caret_browsing() {
1601 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1602 uzbl.behave.caret_browsing, NULL);
1606 cmd_cookie_handler() {
1607 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1608 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1609 if ((g_strcmp0(split[0], "sh") == 0) ||
1610 (g_strcmp0(split[0], "spawn") == 0)) {
1611 g_free (uzbl.behave.cookie_handler);
1612 uzbl.behave.cookie_handler =
1613 g_strdup_printf("sync_%s %s", split[0], split[1]);
1620 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1621 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1622 if ((g_strcmp0(split[0], "sh") == 0) ||
1623 (g_strcmp0(split[0], "spawn") == 0)) {
1624 g_free (uzbl.behave.new_window);
1625 uzbl.behave.new_window =
1626 g_strdup_printf("%s %s", split[0], split[1]);
1633 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1638 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1643 if(uzbl.behave.inject_html) {
1644 webkit_web_view_load_html_string (uzbl.gui.web_view,
1645 uzbl.behave.inject_html, NULL);
1654 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1655 uzbl.behave.modmask = 0;
1657 if(uzbl.behave.modkey)
1658 g_free(uzbl.behave.modkey);
1659 uzbl.behave.modkey = buf;
1661 for (i = 0; modkeys[i].key != NULL; i++) {
1662 if (g_strrstr(buf, modkeys[i].key))
1663 uzbl.behave.modmask |= modkeys[i].mask;
1669 if (*uzbl.net.useragent == ' ') {
1670 g_free (uzbl.net.useragent);
1671 uzbl.net.useragent = NULL;
1673 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1674 uzbl.net.useragent, NULL);
1680 if (!uzbl.gui.scrolled_win &&
1684 gtk_widget_ref(uzbl.gui.scrolled_win);
1685 gtk_widget_ref(uzbl.gui.mainbar);
1686 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1687 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1689 if(uzbl.behave.status_top) {
1690 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1691 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1694 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1695 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1697 gtk_widget_unref(uzbl.gui.scrolled_win);
1698 gtk_widget_unref(uzbl.gui.mainbar);
1699 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1704 set_var_value(gchar *name, gchar *val) {
1705 uzbl_cmdprop *c = NULL;
1708 char *invalid_chars = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
1710 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1711 if(!c->writeable) return FALSE;
1713 /* check for the variable type */
1714 if (c->type == TYPE_STR) {
1715 buf = expand(val, 0);
1716 if(*c->ptr) g_free(*c->ptr);
1718 } else if(c->type == TYPE_INT) {
1719 int *ip = (int *)c->ptr;
1720 buf = expand(val, 0);
1721 *ip = (int)strtoul(buf, &endp, 10);
1723 } else if (c->type == TYPE_FLOAT) {
1724 float *fp = (float *)c->ptr;
1725 buf = expand(val, 0);
1726 *fp = strtod(buf, &endp);
1730 /* invoke a command specific function */
1731 if(c->func) c->func();
1733 /* check wether name violates our naming scheme */
1734 if(strpbrk(name, invalid_chars)) {
1735 if (uzbl.state.verbose)
1736 printf("Invalid variable name\n");
1741 c = malloc(sizeof(uzbl_cmdprop));
1746 buf = expand(val, 0);
1748 g_hash_table_insert(uzbl.comm.proto_var,
1749 g_strdup(name), (gpointer) c);
1756 Behaviour *b = &uzbl.behave;
1758 if(b->html_buffer->str) {
1759 webkit_web_view_load_html_string (uzbl.gui.web_view,
1760 b->html_buffer->str, b->base_url);
1761 g_string_free(b->html_buffer, TRUE);
1762 b->html_buffer = g_string_new("");
1766 enum {M_CMD, M_HTML};
1768 parse_cmd_line(const char *ctl_line, GString *result) {
1769 Behaviour *b = &uzbl.behave;
1772 if(b->mode == M_HTML) {
1773 len = strlen(b->html_endmarker);
1774 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1775 if(len == strlen(ctl_line)-1 &&
1776 !strncmp(b->html_endmarker, ctl_line, len)) {
1778 set_var_value("mode", "0");
1783 set_timeout(b->html_timeout);
1784 g_string_append(b->html_buffer, ctl_line);
1787 else if((ctl_line[0] == '#') /* Comments */
1788 || (ctl_line[0] == ' ')
1789 || (ctl_line[0] == '\n'))
1790 ; /* ignore these lines */
1791 else { /* parse a command */
1793 gchar **tokens = NULL;
1794 len = strlen(ctl_line);
1796 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1797 ctlstrip = g_strndup(ctl_line, len - 1);
1798 else ctlstrip = g_strdup(ctl_line);
1800 tokens = g_strsplit(ctlstrip, " ", 2);
1801 parse_command(tokens[0], tokens[1], result);
1808 build_stream_name(int type, const gchar* dir) {
1809 State *s = &uzbl.state;
1813 str = g_strdup_printf
1814 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1815 } else if (type == SOCKET) {
1816 str = g_strdup_printf
1817 ("%s/uzbl_socket_%s", dir, s->instance_name);
1823 control_fifo(GIOChannel *gio, GIOCondition condition) {
1824 if (uzbl.state.verbose)
1825 printf("triggered\n");
1830 if (condition & G_IO_HUP)
1831 g_error ("Fifo: Read end of pipe died!\n");
1834 g_error ("Fifo: GIOChannel broke\n");
1836 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1837 if (ret == G_IO_STATUS_ERROR) {
1838 g_error ("Fifo: Error reading: %s\n", err->message);
1842 parse_cmd_line(ctl_line, NULL);
1849 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1850 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1851 if (unlink(uzbl.comm.fifo_path) == -1)
1852 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1853 g_free(uzbl.comm.fifo_path);
1854 uzbl.comm.fifo_path = NULL;
1857 GIOChannel *chan = NULL;
1858 GError *error = NULL;
1859 gchar *path = build_stream_name(FIFO, dir);
1861 if (!file_exists(path)) {
1862 if (mkfifo (path, 0666) == 0) {
1863 // 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.
1864 chan = g_io_channel_new_file(path, "r+", &error);
1866 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1867 if (uzbl.state.verbose)
1868 printf ("init_fifo: created successfully as %s\n", path);
1869 uzbl.comm.fifo_path = path;
1871 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1872 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1873 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1874 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1876 /* if we got this far, there was an error; cleanup */
1877 if (error) g_error_free (error);
1884 control_stdin(GIOChannel *gio, GIOCondition condition) {
1886 gchar *ctl_line = NULL;
1889 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1890 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1893 parse_cmd_line(ctl_line, NULL);
1901 GIOChannel *chan = NULL;
1902 GError *error = NULL;
1904 chan = g_io_channel_unix_new(fileno(stdin));
1906 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1907 g_error ("Stdin: could not add watch\n");
1909 if (uzbl.state.verbose)
1910 printf ("Stdin: watch added successfully\n");
1913 g_error ("Stdin: Error while opening: %s\n", error->message);
1915 if (error) g_error_free (error);
1919 control_socket(GIOChannel *chan) {
1920 struct sockaddr_un remote;
1921 unsigned int t = sizeof(remote);
1923 GIOChannel *clientchan;
1925 clientsock = accept (g_io_channel_unix_get_fd(chan),
1926 (struct sockaddr *) &remote, &t);
1928 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1929 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1930 (GIOFunc) control_client_socket, clientchan);
1937 control_client_socket(GIOChannel *clientchan) {
1939 GString *result = g_string_new("");
1940 GError *error = NULL;
1944 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1945 if (ret == G_IO_STATUS_ERROR) {
1946 g_warning ("Error reading: %s\n", error->message);
1947 g_io_channel_shutdown(clientchan, TRUE, &error);
1949 } else if (ret == G_IO_STATUS_EOF) {
1950 /* shutdown and remove channel watch from main loop */
1951 g_io_channel_shutdown(clientchan, TRUE, &error);
1956 parse_cmd_line (ctl_line, result);
1957 g_string_append_c(result, '\n');
1958 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1960 if (ret == G_IO_STATUS_ERROR) {
1961 g_warning ("Error writing: %s", error->message);
1963 g_io_channel_flush(clientchan, &error);
1966 if (error) g_error_free (error);
1967 g_string_free(result, TRUE);
1973 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1974 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1975 if (unlink(uzbl.comm.socket_path) == -1)
1976 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1977 g_free(uzbl.comm.socket_path);
1978 uzbl.comm.socket_path = NULL;
1986 GIOChannel *chan = NULL;
1988 struct sockaddr_un local;
1989 gchar *path = build_stream_name(SOCKET, dir);
1991 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1993 local.sun_family = AF_UNIX;
1994 strcpy (local.sun_path, path);
1995 unlink (local.sun_path);
1997 len = strlen (local.sun_path) + sizeof (local.sun_family);
1998 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1999 if (uzbl.state.verbose)
2000 printf ("init_socket: opened in %s\n", path);
2003 if( (chan = g_io_channel_unix_new(sock)) ) {
2004 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2005 uzbl.comm.socket_path = path;
2008 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2010 /* if we got this far, there was an error; cleanup */
2017 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2018 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2020 // this function may be called very early when the templates are not set (yet), hence the checks
2022 update_title (void) {
2023 Behaviour *b = &uzbl.behave;
2026 if (b->show_status) {
2027 if (b->title_format_short) {
2028 parsed = expand(b->title_format_short, 0);
2029 if (uzbl.gui.main_window)
2030 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2033 if (b->status_format) {
2034 parsed = expand(b->status_format, 0);
2035 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2038 if (b->status_background) {
2040 gdk_color_parse (b->status_background, &color);
2041 //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)
2042 if (uzbl.gui.main_window)
2043 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2044 else if (uzbl.gui.plug)
2045 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2048 if (b->title_format_long) {
2049 parsed = expand(b->title_format_long, 0);
2050 if (uzbl.gui.main_window)
2051 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2058 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2062 retrieve_geometry();
2067 key_press_cb (GtkWidget* window, GdkEventKey* event)
2069 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2073 if (event->type != GDK_KEY_PRESS ||
2074 event->keyval == GDK_Page_Up ||
2075 event->keyval == GDK_Page_Down ||
2076 event->keyval == GDK_Up ||
2077 event->keyval == GDK_Down ||
2078 event->keyval == GDK_Left ||
2079 event->keyval == GDK_Right ||
2080 event->keyval == GDK_Shift_L ||
2081 event->keyval == GDK_Shift_R)
2084 /* turn off insert mode (if always_insert_mode is not used) */
2085 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2086 set_insert_mode(uzbl.behave.always_insert_mode);
2091 if (uzbl.behave.insert_mode &&
2092 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2093 (!uzbl.behave.modmask)
2098 if (event->keyval == GDK_Escape) {
2101 dehilight(uzbl.gui.web_view, NULL, NULL);
2105 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2106 if (event->keyval == GDK_Insert) {
2108 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2109 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2111 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2114 GString* keycmd = g_string_new(uzbl.state.keycmd);
2115 g_string_append (keycmd, str);
2116 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2123 if (event->keyval == GDK_BackSpace)
2124 keycmd_bs(NULL, NULL, NULL);
2126 gboolean key_ret = FALSE;
2127 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2130 GString* keycmd = g_string_new(uzbl.state.keycmd);
2131 g_string_append(keycmd, event->string);
2132 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2135 run_keycmd(key_ret);
2137 if (key_ret) return (!uzbl.behave.insert_mode);
2142 run_keycmd(const gboolean key_ret) {
2143 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2145 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2147 parse_command(act->name, act->param, NULL);
2151 /* try if it's an incremental keycmd or one that takes args, and run it */
2152 GString* short_keys = g_string_new ("");
2153 GString* short_keys_inc = g_string_new ("");
2155 guint len = strlen(uzbl.state.keycmd);
2156 for (i=0; i<len; i++) {
2157 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2158 g_string_assign(short_keys_inc, short_keys->str);
2159 g_string_append_c(short_keys, '_');
2160 g_string_append_c(short_keys_inc, '*');
2162 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2163 /* run normal cmds only if return was pressed */
2164 exec_paramcmd(act, i);
2167 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2168 if (key_ret) /* just quit the incremental command on return */
2170 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2174 g_string_truncate(short_keys, short_keys->len - 1);
2176 g_string_free (short_keys, TRUE);
2177 g_string_free (short_keys_inc, TRUE);
2181 exec_paramcmd(const Action *act, const guint i) {
2182 GString *parampart = g_string_new (uzbl.state.keycmd);
2183 GString *actionname = g_string_new ("");
2184 GString *actionparam = g_string_new ("");
2185 g_string_erase (parampart, 0, i+1);
2187 g_string_printf (actionname, act->name, parampart->str);
2189 g_string_printf (actionparam, act->param, parampart->str);
2190 parse_command(actionname->str, actionparam->str, NULL);
2191 g_string_free(actionname, TRUE);
2192 g_string_free(actionparam, TRUE);
2193 g_string_free(parampart, TRUE);
2201 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2203 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2204 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2205 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2206 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2207 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2208 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2209 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2210 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2211 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2212 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2213 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2220 g->mainbar = gtk_hbox_new (FALSE, 0);
2222 /* keep a reference to the bar so we can re-pack it at runtime*/
2223 //sbar_ref = g_object_ref(g->mainbar);
2225 g->mainbar_label = gtk_label_new ("");
2226 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2227 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2228 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2229 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2230 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2231 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2237 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2238 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2239 gtk_widget_set_name (window, "Uzbl browser");
2240 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2241 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2242 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2249 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2250 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2251 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2258 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2260 If actname is one that calls an external command, this function will inject
2261 newargs in front of the user-provided args in that command line. They will
2262 come become after the body of the script (in sh) or after the name of
2263 the command to execute (in spawn).
2264 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2265 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2267 The return value consist of two strings: the action (sh, ...) and its args.
2269 If act is not one that calls an external command, then the given action merely
2272 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2273 /* Arrr! Here be memory leaks */
2274 gchar *actdup = g_strdup(actname);
2275 g_array_append_val(rets, actdup);
2277 if ((g_strcmp0(actname, "spawn") == 0) ||
2278 (g_strcmp0(actname, "sh") == 0) ||
2279 (g_strcmp0(actname, "sync_spawn") == 0) ||
2280 (g_strcmp0(actname, "sync_sh") == 0)) {
2282 GString *a = g_string_new("");
2283 gchar **spawnparts = split_quoted(origargs, FALSE);
2284 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2285 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2287 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2288 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2290 g_array_append_val(rets, a->str);
2291 g_string_free(a, FALSE);
2292 g_strfreev(spawnparts);
2294 gchar *origdup = g_strdup(origargs);
2295 g_array_append_val(rets, origdup);
2297 return (gchar**)g_array_free(rets, FALSE);
2301 run_handler (const gchar *act, const gchar *args) {
2302 /* Consider this code a temporary hack to make the handlers usable.
2303 In practice, all this splicing, injection, and reconstruction is
2304 inefficient, annoying and hard to manage. Potential pitfalls arise
2305 when the handler specific args 1) are not quoted (the handler
2306 callbacks should take care of this) 2) are quoted but interfere
2307 with the users' own quotation. A more ideal solution is
2308 to refactor parse_command so that it doesn't just take a string
2309 and execute it; rather than that, we should have a function which
2310 returns the argument vector parsed from the string. This vector
2311 could be modified (e.g. insert additional args into it) before
2312 passing it to the next function that actually executes it. Though
2313 it still isn't perfect for chain actions.. will reconsider & re-
2314 factor when I have the time. -duc */
2316 char **parts = g_strsplit(act, " ", 2);
2318 if (g_strcmp0(parts[0], "chain") == 0) {
2319 GString *newargs = g_string_new("");
2320 gchar **chainparts = split_quoted(parts[1], FALSE);
2322 /* for every argument in the chain, inject the handler args
2323 and make sure the new parts are wrapped in quotes */
2324 gchar **cp = chainparts;
2326 gchar *quotless = NULL;
2327 gchar **spliced_quotless = NULL; // sigh -_-;
2328 gchar **inpart = NULL;
2331 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2333 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2334 } else quotless = g_strdup(*cp);
2336 spliced_quotless = g_strsplit(quotless, " ", 2);
2337 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2338 g_strfreev(spliced_quotless);
2340 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2346 parse_command(parts[0], &(newargs->str[1]), NULL);
2347 g_string_free(newargs, TRUE);
2348 g_strfreev(chainparts);
2351 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2352 parse_command(inparts[0], inparts[1], NULL);
2360 add_binding (const gchar *key, const gchar *act) {
2361 char **parts = g_strsplit(act, " ", 2);
2368 if (uzbl.state.verbose)
2369 printf ("Binding %-10s : %s\n", key, act);
2370 action = new_action(parts[0], parts[1]);
2372 if (g_hash_table_remove (uzbl.bindings, key))
2373 g_warning ("Overwriting existing binding for \"%s\"", key);
2374 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2379 get_xdg_var (XDG_Var xdg) {
2380 const gchar* actual_value = getenv (xdg.environmental);
2381 const gchar* home = getenv ("HOME");
2382 gchar* return_value;
2384 if (! actual_value || strcmp (actual_value, "") == 0) {
2385 if (xdg.default_value) {
2386 return_value = str_replace ("~", home, xdg.default_value);
2388 return_value = NULL;
2391 return_value = str_replace("~", home, actual_value);
2394 return return_value;
2398 find_xdg_file (int xdg_type, char* filename) {
2399 /* xdg_type = 0 => config
2400 xdg_type = 1 => data
2401 xdg_type = 2 => cache*/
2403 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2404 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2407 gchar* temporary_string;
2411 if (! file_exists (temporary_file) && xdg_type != 2) {
2412 buf = get_xdg_var (XDG[3 + xdg_type]);
2413 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2416 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2417 g_free (temporary_file);
2418 temporary_file = g_strconcat (temporary_string, filename, NULL);
2422 //g_free (temporary_string); - segfaults.
2424 if (file_exists (temporary_file)) {
2425 return temporary_file;
2432 State *s = &uzbl.state;
2433 Network *n = &uzbl.net;
2435 for (i = 0; default_config[i].command != NULL; i++) {
2436 parse_cmd_line(default_config[i].command, NULL);
2439 if (g_strcmp0(s->config_file, "-") == 0) {
2440 s->config_file = NULL;
2444 else if (!s->config_file) {
2445 s->config_file = find_xdg_file (0, "/uzbl/config");
2448 if (s->config_file) {
2449 GArray* lines = read_file_by_line (s->config_file);
2453 while ((line = g_array_index(lines, gchar*, i))) {
2454 parse_cmd_line (line, NULL);
2458 g_array_free (lines, TRUE);
2460 if (uzbl.state.verbose)
2461 printf ("No configuration file loaded.\n");
2464 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2467 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2470 if (!uzbl.behave.cookie_handler)
2473 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2474 GString *s = g_string_new ("");
2475 SoupURI * soup_uri = soup_message_get_uri(msg);
2476 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2477 run_handler(uzbl.behave.cookie_handler, s->str);
2479 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2480 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2481 if ( p != NULL ) *p = '\0';
2482 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2484 if (uzbl.comm.sync_stdout)
2485 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2487 g_string_free(s, TRUE);
2491 save_cookies (SoupMessage *msg, gpointer user_data){
2495 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2496 cookie = soup_cookie_to_set_cookie_header(ck->data);
2497 SoupURI * soup_uri = soup_message_get_uri(msg);
2498 GString *s = g_string_new ("");
2499 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2500 run_handler(uzbl.behave.cookie_handler, s->str);
2502 g_string_free(s, TRUE);
2507 /* --- WEBINSPECTOR --- */
2509 hide_window_cb(GtkWidget *widget, gpointer data) {
2512 gtk_widget_hide(widget);
2516 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2519 (void) web_inspector;
2520 GtkWidget* scrolled_window;
2521 GtkWidget* new_web_view;
2524 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2525 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2526 G_CALLBACK(hide_window_cb), NULL);
2528 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2529 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2530 gtk_widget_show(g->inspector_window);
2532 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2533 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2534 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2535 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2536 gtk_widget_show(scrolled_window);
2538 new_web_view = webkit_web_view_new();
2539 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2541 return WEBKIT_WEB_VIEW(new_web_view);
2545 inspector_show_window_cb (WebKitWebInspector* inspector){
2547 gtk_widget_show(uzbl.gui.inspector_window);
2551 /* TODO: Add variables and code to make use of these functions */
2553 inspector_close_window_cb (WebKitWebInspector* inspector){
2559 inspector_attach_window_cb (WebKitWebInspector* inspector){
2565 inspector_detach_window_cb (WebKitWebInspector* inspector){
2571 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2577 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2583 set_up_inspector() {
2585 WebKitWebSettings *settings = view_settings();
2586 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2588 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2589 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2590 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2591 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2592 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2593 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2594 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2596 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2600 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2602 uzbl_cmdprop *c = v;
2607 if(c->type == TYPE_STR)
2608 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2609 else if(c->type == TYPE_INT)
2610 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2611 else if(c->type == TYPE_FLOAT)
2612 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2616 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2620 printf("bind %s = %s %s\n", (char *)k ,
2621 (char *)a->name, a->param?(char *)a->param:"");
2626 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2627 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2631 retrieve_geometry() {
2633 GString *buf = g_string_new("");
2635 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2636 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2638 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2640 if(uzbl.gui.geometry)
2641 g_free(uzbl.gui.geometry);
2642 uzbl.gui.geometry = g_string_free(buf, FALSE);
2645 /* set up gtk, gobject, variable defaults and other things that tests and other
2646 * external applications need to do anyhow */
2648 initialize(int argc, char *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 ("[ uri ] - load a uri by default");
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 if (uzbl.behave.print_version) {
2662 printf("Commit: %s\n", COMMIT);
2666 /* initialize hash table */
2667 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2669 uzbl.net.soup_session = webkit_get_default_session();
2670 uzbl.state.keycmd = g_strdup("");
2672 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2673 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2674 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2675 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2676 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2677 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2679 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2680 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2681 uzbl.gui.sbar.progress_w = 10;
2683 /* HTML mode defaults*/
2684 uzbl.behave.html_buffer = g_string_new("");
2685 uzbl.behave.html_endmarker = g_strdup(".");
2686 uzbl.behave.html_timeout = 60;
2687 uzbl.behave.base_url = g_strdup("http://invalid");
2689 /* default mode indicators */
2690 uzbl.behave.insert_indicator = g_strdup("I");
2691 uzbl.behave.cmd_indicator = g_strdup("C");
2693 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2694 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2695 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2696 uzbl.info.arch = ARCH;
2697 uzbl.info.commit = COMMIT;
2700 make_var_to_name_hash();
2705 #ifndef UZBL_LIBRARY
2708 main (int argc, char* argv[]) {
2709 initialize(argc, argv);
2711 gtk_init (&argc, &argv);
2713 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2714 //main_window_ref = g_object_ref(scrolled_window);
2715 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2716 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2718 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2719 GTK_WIDGET (uzbl.gui.web_view));
2721 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2725 /* initial packing */
2726 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2727 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2729 if (uzbl.state.socket_id) {
2730 uzbl.gui.plug = create_plug ();
2731 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2732 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2734 uzbl.gui.main_window = create_window ();
2735 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2736 gtk_widget_show_all (uzbl.gui.main_window);
2737 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2740 if(!uzbl.state.instance_name)
2741 uzbl.state.instance_name = itos((int)uzbl.xwin);
2743 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2745 if (uzbl.state.verbose) {
2746 printf("Uzbl start location: %s\n", argv[0]);
2747 if (uzbl.state.socket_id)
2748 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2750 printf("window_id %i\n",(int) uzbl.xwin);
2751 printf("pid %i\n", getpid ());
2752 printf("name: %s\n", uzbl.state.instance_name);
2755 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2756 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2757 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2758 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2759 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2761 if(uzbl.gui.geometry)
2764 retrieve_geometry();
2766 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2767 if (argc > 1 && !uzbl.state.uri)
2768 uri_override = g_strdup(argv[1]);
2769 gboolean verbose_override = uzbl.state.verbose;
2772 set_insert_mode(FALSE);
2774 if (!uzbl.behave.show_status)
2775 gtk_widget_hide(uzbl.gui.mainbar);
2782 if (verbose_override > uzbl.state.verbose)
2783 uzbl.state.verbose = verbose_override;
2786 set_var_value("uri", uri_override);
2787 g_free(uri_override);
2788 } else if (uzbl.state.uri)
2789 cmd_load_uri(uzbl.gui.web_view, NULL);
2794 return EXIT_SUCCESS;
2798 /* vi: set et ts=4: */