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);
1747 c->ptr = malloc(sizeof(char *));
1749 g_hash_table_insert(uzbl.comm.proto_var,
1750 g_strdup(name), (gpointer) c);
1757 Behaviour *b = &uzbl.behave;
1759 if(b->html_buffer->str) {
1760 webkit_web_view_load_html_string (uzbl.gui.web_view,
1761 b->html_buffer->str, b->base_url);
1762 g_string_free(b->html_buffer, TRUE);
1763 b->html_buffer = g_string_new("");
1767 enum {M_CMD, M_HTML};
1769 parse_cmd_line(const char *ctl_line, GString *result) {
1770 Behaviour *b = &uzbl.behave;
1773 if(b->mode == M_HTML) {
1774 len = strlen(b->html_endmarker);
1775 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1776 if(len == strlen(ctl_line)-1 &&
1777 !strncmp(b->html_endmarker, ctl_line, len)) {
1779 set_var_value("mode", "0");
1784 set_timeout(b->html_timeout);
1785 g_string_append(b->html_buffer, ctl_line);
1788 else if((ctl_line[0] == '#') /* Comments */
1789 || (ctl_line[0] == ' ')
1790 || (ctl_line[0] == '\n'))
1791 ; /* ignore these lines */
1792 else { /* parse a command */
1794 gchar **tokens = NULL;
1795 len = strlen(ctl_line);
1797 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1798 ctlstrip = g_strndup(ctl_line, len - 1);
1799 else ctlstrip = g_strdup(ctl_line);
1801 tokens = g_strsplit(ctlstrip, " ", 2);
1802 parse_command(tokens[0], tokens[1], result);
1809 build_stream_name(int type, const gchar* dir) {
1810 State *s = &uzbl.state;
1814 str = g_strdup_printf
1815 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1816 } else if (type == SOCKET) {
1817 str = g_strdup_printf
1818 ("%s/uzbl_socket_%s", dir, s->instance_name);
1824 control_fifo(GIOChannel *gio, GIOCondition condition) {
1825 if (uzbl.state.verbose)
1826 printf("triggered\n");
1831 if (condition & G_IO_HUP)
1832 g_error ("Fifo: Read end of pipe died!\n");
1835 g_error ("Fifo: GIOChannel broke\n");
1837 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1838 if (ret == G_IO_STATUS_ERROR) {
1839 g_error ("Fifo: Error reading: %s\n", err->message);
1843 parse_cmd_line(ctl_line, NULL);
1850 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1851 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1852 if (unlink(uzbl.comm.fifo_path) == -1)
1853 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1854 g_free(uzbl.comm.fifo_path);
1855 uzbl.comm.fifo_path = NULL;
1858 GIOChannel *chan = NULL;
1859 GError *error = NULL;
1860 gchar *path = build_stream_name(FIFO, dir);
1862 if (!file_exists(path)) {
1863 if (mkfifo (path, 0666) == 0) {
1864 // 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.
1865 chan = g_io_channel_new_file(path, "r+", &error);
1867 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1868 if (uzbl.state.verbose)
1869 printf ("init_fifo: created successfully as %s\n", path);
1870 uzbl.comm.fifo_path = path;
1872 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1873 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1874 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1875 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1877 /* if we got this far, there was an error; cleanup */
1878 if (error) g_error_free (error);
1885 control_stdin(GIOChannel *gio, GIOCondition condition) {
1887 gchar *ctl_line = NULL;
1890 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1891 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1894 parse_cmd_line(ctl_line, NULL);
1902 GIOChannel *chan = NULL;
1903 GError *error = NULL;
1905 chan = g_io_channel_unix_new(fileno(stdin));
1907 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1908 g_error ("Stdin: could not add watch\n");
1910 if (uzbl.state.verbose)
1911 printf ("Stdin: watch added successfully\n");
1914 g_error ("Stdin: Error while opening: %s\n", error->message);
1916 if (error) g_error_free (error);
1920 control_socket(GIOChannel *chan) {
1921 struct sockaddr_un remote;
1922 unsigned int t = sizeof(remote);
1924 GIOChannel *clientchan;
1926 clientsock = accept (g_io_channel_unix_get_fd(chan),
1927 (struct sockaddr *) &remote, &t);
1929 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1930 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1931 (GIOFunc) control_client_socket, clientchan);
1938 control_client_socket(GIOChannel *clientchan) {
1940 GString *result = g_string_new("");
1941 GError *error = NULL;
1945 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1946 if (ret == G_IO_STATUS_ERROR) {
1947 g_warning ("Error reading: %s\n", error->message);
1948 g_io_channel_shutdown(clientchan, TRUE, &error);
1950 } else if (ret == G_IO_STATUS_EOF) {
1951 /* shutdown and remove channel watch from main loop */
1952 g_io_channel_shutdown(clientchan, TRUE, &error);
1957 parse_cmd_line (ctl_line, result);
1958 g_string_append_c(result, '\n');
1959 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1961 if (ret == G_IO_STATUS_ERROR) {
1962 g_warning ("Error writing: %s", error->message);
1964 g_io_channel_flush(clientchan, &error);
1967 if (error) g_error_free (error);
1968 g_string_free(result, TRUE);
1974 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1975 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1976 if (unlink(uzbl.comm.socket_path) == -1)
1977 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1978 g_free(uzbl.comm.socket_path);
1979 uzbl.comm.socket_path = NULL;
1987 GIOChannel *chan = NULL;
1989 struct sockaddr_un local;
1990 gchar *path = build_stream_name(SOCKET, dir);
1992 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1994 local.sun_family = AF_UNIX;
1995 strcpy (local.sun_path, path);
1996 unlink (local.sun_path);
1998 len = strlen (local.sun_path) + sizeof (local.sun_family);
1999 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2000 if (uzbl.state.verbose)
2001 printf ("init_socket: opened in %s\n", path);
2004 if( (chan = g_io_channel_unix_new(sock)) ) {
2005 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2006 uzbl.comm.socket_path = path;
2009 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2011 /* if we got this far, there was an error; cleanup */
2018 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2019 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2021 // this function may be called very early when the templates are not set (yet), hence the checks
2023 update_title (void) {
2024 Behaviour *b = &uzbl.behave;
2027 if (b->show_status) {
2028 if (b->title_format_short) {
2029 parsed = expand(b->title_format_short, 0);
2030 if (uzbl.gui.main_window)
2031 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2034 if (b->status_format) {
2035 parsed = expand(b->status_format, 0);
2036 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2039 if (b->status_background) {
2041 gdk_color_parse (b->status_background, &color);
2042 //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)
2043 if (uzbl.gui.main_window)
2044 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2045 else if (uzbl.gui.plug)
2046 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2049 if (b->title_format_long) {
2050 parsed = expand(b->title_format_long, 0);
2051 if (uzbl.gui.main_window)
2052 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2059 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2063 retrieve_geometry();
2068 key_press_cb (GtkWidget* window, GdkEventKey* event)
2070 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2074 if (event->type != GDK_KEY_PRESS ||
2075 event->keyval == GDK_Page_Up ||
2076 event->keyval == GDK_Page_Down ||
2077 event->keyval == GDK_Up ||
2078 event->keyval == GDK_Down ||
2079 event->keyval == GDK_Left ||
2080 event->keyval == GDK_Right ||
2081 event->keyval == GDK_Shift_L ||
2082 event->keyval == GDK_Shift_R)
2085 /* turn off insert mode (if always_insert_mode is not used) */
2086 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2087 set_insert_mode(uzbl.behave.always_insert_mode);
2092 if (uzbl.behave.insert_mode &&
2093 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2094 (!uzbl.behave.modmask)
2099 if (event->keyval == GDK_Escape) {
2102 dehilight(uzbl.gui.web_view, NULL, NULL);
2106 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2107 if (event->keyval == GDK_Insert) {
2109 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2110 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2112 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2115 GString* keycmd = g_string_new(uzbl.state.keycmd);
2116 g_string_append (keycmd, str);
2117 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2124 if (event->keyval == GDK_BackSpace)
2125 keycmd_bs(NULL, NULL, NULL);
2127 gboolean key_ret = FALSE;
2128 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2131 GString* keycmd = g_string_new(uzbl.state.keycmd);
2132 g_string_append(keycmd, event->string);
2133 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2136 run_keycmd(key_ret);
2138 if (key_ret) return (!uzbl.behave.insert_mode);
2143 run_keycmd(const gboolean key_ret) {
2144 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2146 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2148 parse_command(act->name, act->param, NULL);
2152 /* try if it's an incremental keycmd or one that takes args, and run it */
2153 GString* short_keys = g_string_new ("");
2154 GString* short_keys_inc = g_string_new ("");
2156 guint len = strlen(uzbl.state.keycmd);
2157 for (i=0; i<len; i++) {
2158 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2159 g_string_assign(short_keys_inc, short_keys->str);
2160 g_string_append_c(short_keys, '_');
2161 g_string_append_c(short_keys_inc, '*');
2163 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2164 /* run normal cmds only if return was pressed */
2165 exec_paramcmd(act, i);
2168 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2169 if (key_ret) /* just quit the incremental command on return */
2171 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2175 g_string_truncate(short_keys, short_keys->len - 1);
2177 g_string_free (short_keys, TRUE);
2178 g_string_free (short_keys_inc, TRUE);
2182 exec_paramcmd(const Action *act, const guint i) {
2183 GString *parampart = g_string_new (uzbl.state.keycmd);
2184 GString *actionname = g_string_new ("");
2185 GString *actionparam = g_string_new ("");
2186 g_string_erase (parampart, 0, i+1);
2188 g_string_printf (actionname, act->name, parampart->str);
2190 g_string_printf (actionparam, act->param, parampart->str);
2191 parse_command(actionname->str, actionparam->str, NULL);
2192 g_string_free(actionname, TRUE);
2193 g_string_free(actionparam, TRUE);
2194 g_string_free(parampart, TRUE);
2202 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2204 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2205 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2206 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2207 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2208 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2209 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2210 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2211 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2212 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2213 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2214 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2221 g->mainbar = gtk_hbox_new (FALSE, 0);
2223 /* keep a reference to the bar so we can re-pack it at runtime*/
2224 //sbar_ref = g_object_ref(g->mainbar);
2226 g->mainbar_label = gtk_label_new ("");
2227 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2228 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2229 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2230 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2231 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2232 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2238 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2239 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2240 gtk_widget_set_name (window, "Uzbl browser");
2241 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2242 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2243 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2250 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2251 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2252 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2259 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2261 If actname is one that calls an external command, this function will inject
2262 newargs in front of the user-provided args in that command line. They will
2263 come become after the body of the script (in sh) or after the name of
2264 the command to execute (in spawn).
2265 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2266 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2268 The return value consist of two strings: the action (sh, ...) and its args.
2270 If act is not one that calls an external command, then the given action merely
2273 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2274 /* Arrr! Here be memory leaks */
2275 gchar *actdup = g_strdup(actname);
2276 g_array_append_val(rets, actdup);
2278 if ((g_strcmp0(actname, "spawn") == 0) ||
2279 (g_strcmp0(actname, "sh") == 0) ||
2280 (g_strcmp0(actname, "sync_spawn") == 0) ||
2281 (g_strcmp0(actname, "sync_sh") == 0)) {
2283 GString *a = g_string_new("");
2284 gchar **spawnparts = split_quoted(origargs, FALSE);
2285 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2286 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2288 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2289 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2291 g_array_append_val(rets, a->str);
2292 g_string_free(a, FALSE);
2293 g_strfreev(spawnparts);
2295 gchar *origdup = g_strdup(origargs);
2296 g_array_append_val(rets, origdup);
2298 return (gchar**)g_array_free(rets, FALSE);
2302 run_handler (const gchar *act, const gchar *args) {
2303 /* Consider this code a temporary hack to make the handlers usable.
2304 In practice, all this splicing, injection, and reconstruction is
2305 inefficient, annoying and hard to manage. Potential pitfalls arise
2306 when the handler specific args 1) are not quoted (the handler
2307 callbacks should take care of this) 2) are quoted but interfere
2308 with the users' own quotation. A more ideal solution is
2309 to refactor parse_command so that it doesn't just take a string
2310 and execute it; rather than that, we should have a function which
2311 returns the argument vector parsed from the string. This vector
2312 could be modified (e.g. insert additional args into it) before
2313 passing it to the next function that actually executes it. Though
2314 it still isn't perfect for chain actions.. will reconsider & re-
2315 factor when I have the time. -duc */
2317 char **parts = g_strsplit(act, " ", 2);
2319 if (g_strcmp0(parts[0], "chain") == 0) {
2320 GString *newargs = g_string_new("");
2321 gchar **chainparts = split_quoted(parts[1], FALSE);
2323 /* for every argument in the chain, inject the handler args
2324 and make sure the new parts are wrapped in quotes */
2325 gchar **cp = chainparts;
2327 gchar *quotless = NULL;
2328 gchar **spliced_quotless = NULL; // sigh -_-;
2329 gchar **inpart = NULL;
2332 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2334 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2335 } else quotless = g_strdup(*cp);
2337 spliced_quotless = g_strsplit(quotless, " ", 2);
2338 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2339 g_strfreev(spliced_quotless);
2341 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2347 parse_command(parts[0], &(newargs->str[1]), NULL);
2348 g_string_free(newargs, TRUE);
2349 g_strfreev(chainparts);
2352 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2353 parse_command(inparts[0], inparts[1], NULL);
2361 add_binding (const gchar *key, const gchar *act) {
2362 char **parts = g_strsplit(act, " ", 2);
2369 if (uzbl.state.verbose)
2370 printf ("Binding %-10s : %s\n", key, act);
2371 action = new_action(parts[0], parts[1]);
2373 if (g_hash_table_remove (uzbl.bindings, key))
2374 g_warning ("Overwriting existing binding for \"%s\"", key);
2375 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2380 get_xdg_var (XDG_Var xdg) {
2381 const gchar* actual_value = getenv (xdg.environmental);
2382 const gchar* home = getenv ("HOME");
2383 gchar* return_value;
2385 if (! actual_value || strcmp (actual_value, "") == 0) {
2386 if (xdg.default_value) {
2387 return_value = str_replace ("~", home, xdg.default_value);
2389 return_value = NULL;
2392 return_value = str_replace("~", home, actual_value);
2395 return return_value;
2399 find_xdg_file (int xdg_type, char* filename) {
2400 /* xdg_type = 0 => config
2401 xdg_type = 1 => data
2402 xdg_type = 2 => cache*/
2404 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2405 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2408 gchar* temporary_string;
2412 if (! file_exists (temporary_file) && xdg_type != 2) {
2413 buf = get_xdg_var (XDG[3 + xdg_type]);
2414 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2417 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2418 g_free (temporary_file);
2419 temporary_file = g_strconcat (temporary_string, filename, NULL);
2423 //g_free (temporary_string); - segfaults.
2425 if (file_exists (temporary_file)) {
2426 return temporary_file;
2433 State *s = &uzbl.state;
2434 Network *n = &uzbl.net;
2436 for (i = 0; default_config[i].command != NULL; i++) {
2437 parse_cmd_line(default_config[i].command, NULL);
2440 if (g_strcmp0(s->config_file, "-") == 0) {
2441 s->config_file = NULL;
2445 else if (!s->config_file) {
2446 s->config_file = find_xdg_file (0, "/uzbl/config");
2449 if (s->config_file) {
2450 GArray* lines = read_file_by_line (s->config_file);
2454 while ((line = g_array_index(lines, gchar*, i))) {
2455 parse_cmd_line (line, NULL);
2459 g_array_free (lines, TRUE);
2461 if (uzbl.state.verbose)
2462 printf ("No configuration file loaded.\n");
2465 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2468 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2471 if (!uzbl.behave.cookie_handler)
2474 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2475 GString *s = g_string_new ("");
2476 SoupURI * soup_uri = soup_message_get_uri(msg);
2477 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2478 run_handler(uzbl.behave.cookie_handler, s->str);
2480 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2481 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2482 if ( p != NULL ) *p = '\0';
2483 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2485 if (uzbl.comm.sync_stdout)
2486 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2488 g_string_free(s, TRUE);
2492 save_cookies (SoupMessage *msg, gpointer user_data){
2496 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2497 cookie = soup_cookie_to_set_cookie_header(ck->data);
2498 SoupURI * soup_uri = soup_message_get_uri(msg);
2499 GString *s = g_string_new ("");
2500 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2501 run_handler(uzbl.behave.cookie_handler, s->str);
2503 g_string_free(s, TRUE);
2508 /* --- WEBINSPECTOR --- */
2510 hide_window_cb(GtkWidget *widget, gpointer data) {
2513 gtk_widget_hide(widget);
2517 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2520 (void) web_inspector;
2521 GtkWidget* scrolled_window;
2522 GtkWidget* new_web_view;
2525 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2526 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2527 G_CALLBACK(hide_window_cb), NULL);
2529 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2530 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2531 gtk_widget_show(g->inspector_window);
2533 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2534 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2535 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2536 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2537 gtk_widget_show(scrolled_window);
2539 new_web_view = webkit_web_view_new();
2540 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2542 return WEBKIT_WEB_VIEW(new_web_view);
2546 inspector_show_window_cb (WebKitWebInspector* inspector){
2548 gtk_widget_show(uzbl.gui.inspector_window);
2552 /* TODO: Add variables and code to make use of these functions */
2554 inspector_close_window_cb (WebKitWebInspector* inspector){
2560 inspector_attach_window_cb (WebKitWebInspector* inspector){
2566 inspector_detach_window_cb (WebKitWebInspector* inspector){
2572 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2578 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2584 set_up_inspector() {
2586 WebKitWebSettings *settings = view_settings();
2587 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2589 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2590 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2591 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2592 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2593 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2594 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2595 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2597 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2601 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2603 uzbl_cmdprop *c = v;
2608 if(c->type == TYPE_STR)
2609 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2610 else if(c->type == TYPE_INT)
2611 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2612 else if(c->type == TYPE_FLOAT)
2613 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2617 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2621 printf("bind %s = %s %s\n", (char *)k ,
2622 (char *)a->name, a->param?(char *)a->param:"");
2627 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2628 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2632 retrieve_geometry() {
2634 GString *buf = g_string_new("");
2636 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2637 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2639 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2641 if(uzbl.gui.geometry)
2642 g_free(uzbl.gui.geometry);
2643 uzbl.gui.geometry = g_string_free(buf, FALSE);
2646 /* set up gtk, gobject, variable defaults and other things that tests and other
2647 * external applications need to do anyhow */
2649 initialize(int argc, char *argv[]) {
2650 if (!g_thread_supported ())
2651 g_thread_init (NULL);
2652 uzbl.state.executable_path = g_strdup(argv[0]);
2653 uzbl.state.selected_url = NULL;
2654 uzbl.state.searchtx = NULL;
2656 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2657 g_option_context_add_main_entries (context, entries, NULL);
2658 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2659 g_option_context_parse (context, &argc, &argv, NULL);
2660 g_option_context_free(context);
2662 if (uzbl.behave.print_version) {
2663 printf("Commit: %s\n", COMMIT);
2667 /* initialize hash table */
2668 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2670 uzbl.net.soup_session = webkit_get_default_session();
2671 uzbl.state.keycmd = g_strdup("");
2673 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2674 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2675 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2676 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2677 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2678 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2680 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2681 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2682 uzbl.gui.sbar.progress_w = 10;
2684 /* HTML mode defaults*/
2685 uzbl.behave.html_buffer = g_string_new("");
2686 uzbl.behave.html_endmarker = g_strdup(".");
2687 uzbl.behave.html_timeout = 60;
2688 uzbl.behave.base_url = g_strdup("http://invalid");
2690 /* default mode indicators */
2691 uzbl.behave.insert_indicator = g_strdup("I");
2692 uzbl.behave.cmd_indicator = g_strdup("C");
2694 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2695 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2696 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2697 uzbl.info.arch = ARCH;
2698 uzbl.info.commit = COMMIT;
2701 make_var_to_name_hash();
2706 #ifndef UZBL_LIBRARY
2709 main (int argc, char* argv[]) {
2710 initialize(argc, argv);
2712 gtk_init (&argc, &argv);
2714 uzbl.gui.scrolled_win = gtk_scrolled_window_new (NULL, NULL);
2715 //main_window_ref = g_object_ref(scrolled_window);
2716 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (uzbl.gui.scrolled_win),
2717 GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2719 gtk_container_add (GTK_CONTAINER (uzbl.gui.scrolled_win),
2720 GTK_WIDGET (uzbl.gui.web_view));
2722 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2726 /* initial packing */
2727 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2728 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2730 if (uzbl.state.socket_id) {
2731 uzbl.gui.plug = create_plug ();
2732 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2733 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2735 uzbl.gui.main_window = create_window ();
2736 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2737 gtk_widget_show_all (uzbl.gui.main_window);
2738 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2741 if(!uzbl.state.instance_name)
2742 uzbl.state.instance_name = itos((int)uzbl.xwin);
2744 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2746 if (uzbl.state.verbose) {
2747 printf("Uzbl start location: %s\n", argv[0]);
2748 if (uzbl.state.socket_id)
2749 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2751 printf("window_id %i\n",(int) uzbl.xwin);
2752 printf("pid %i\n", getpid ());
2753 printf("name: %s\n", uzbl.state.instance_name);
2756 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2757 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2758 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2759 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2760 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2762 if(uzbl.gui.geometry)
2765 retrieve_geometry();
2767 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2768 if (argc > 1 && !uzbl.state.uri)
2769 uri_override = g_strdup(argv[1]);
2770 gboolean verbose_override = uzbl.state.verbose;
2773 set_insert_mode(FALSE);
2775 if (!uzbl.behave.show_status)
2776 gtk_widget_hide(uzbl.gui.mainbar);
2783 if (verbose_override > uzbl.state.verbose)
2784 uzbl.state.verbose = verbose_override;
2787 set_var_value("uri", uri_override);
2788 g_free(uri_override);
2789 } else if (uzbl.state.uri)
2790 cmd_load_uri(uzbl.gui.web_view, NULL);
2795 return EXIT_SUCCESS;
2799 /* vi: set et ts=4: */