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 'set uri = URI')", "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 "Config file (this is pretty much equivalent to uzbl < FILE )", "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 */
84 typedef const struct {
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 { "default_font_family", PTR_V(uzbl.behave.default_font_family, STR, 1, cmd_default_font_family)},
151 { "monospace_font_family", PTR_V(uzbl.behave.monospace_font_family, STR, 1, cmd_monospace_font_family)},
152 { "cursive_font_family", PTR_V(uzbl.behave.cursive_font_family, STR, 1, cmd_cursive_font_family)},
153 { "sans_serif_font_family", PTR_V(uzbl.behave.sans_serif_font_family, STR, 1, cmd_sans_serif_font_family)},
154 { "serif_font_family", PTR_V(uzbl.behave.serif_font_family, STR, 1, cmd_serif_font_family)},
155 { "fantasy_font_family", PTR_V(uzbl.behave.fantasy_font_family, STR, 1, cmd_fantasy_font_family)},
156 { "monospace_size", PTR_V(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
157 { "minimum_font_size", PTR_V(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
158 { "disable_plugins", PTR_V(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
159 { "disable_scripts", PTR_V(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
160 { "autoload_images", PTR_V(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
161 { "autoshrink_images", PTR_V(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
162 { "enable_spellcheck", PTR_V(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
163 { "enable_private", PTR_V(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
164 { "print_backgrounds", PTR_V(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
165 { "stylesheet_uri", PTR_V(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
166 { "resizable_text_areas", PTR_V(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
167 { "default_encoding", PTR_V(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
168 { "enforce_96_dpi", PTR_V(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
169 { "caret_browsing", PTR_V(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
171 /* constants (not dumpable or writeable) */
172 { "WEBKIT_MAJOR", PTR_C(uzbl.info.webkit_major, INT, NULL)},
173 { "WEBKIT_MINOR", PTR_C(uzbl.info.webkit_minor, INT, NULL)},
174 { "WEBKIT_MICRO", PTR_C(uzbl.info.webkit_micro, INT, NULL)},
175 { "ARCH_UZBL", PTR_C(uzbl.info.arch, STR, NULL)},
176 { "COMMIT", PTR_C(uzbl.info.commit, STR, NULL)},
177 { "LOAD_PROGRESS", PTR_C(uzbl.gui.sbar.load_progress, INT, NULL)},
178 { "LOAD_PROGRESSBAR", PTR_C(uzbl.gui.sbar.progress_bar, STR, NULL)},
179 { "TITLE", PTR_C(uzbl.gui.main_title, STR, NULL)},
180 { "SELECTED_URI", PTR_C(uzbl.state.selected_url, STR, NULL)},
181 { "MODE", PTR_C(uzbl.gui.sbar.mode_indicator, STR, NULL)},
182 { "NAME", PTR_C(uzbl.state.instance_name, STR, NULL)},
184 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}}
185 }, *n2v_p = var_name_to_ptr;
192 { "SHIFT", GDK_SHIFT_MASK }, // shift
193 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
194 { "CONTROL", GDK_CONTROL_MASK }, // control
195 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
196 { "MOD2", GDK_MOD2_MASK }, // 5th mod
197 { "MOD3", GDK_MOD3_MASK }, // 6th mod
198 { "MOD4", GDK_MOD4_MASK }, // 7th mod
199 { "MOD5", GDK_MOD5_MASK }, // 8th mod
200 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
201 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
202 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
203 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
204 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
205 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
206 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
207 { "META", GDK_META_MASK }, // meta (since 2.10)
212 /* construct a hash from the var_name_to_ptr array for quick access */
214 make_var_to_name_hash() {
215 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
217 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
222 /* --- UTILITY FUNCTIONS --- */
223 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
225 get_exp_type(gchar *s) {
229 else if(*(s+1) == '{')
230 return EXP_BRACED_VAR;
231 else if(*(s+1) == '<')
233 else if(*(s+1) == '[')
236 return EXP_SIMPLE_VAR;
242 * recurse == 1: don't expand '@(command)@'
243 * recurse == 2: don't expand '@<java script>@'
246 expand(char *s, guint recurse) {
250 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
255 gchar *cmd_stdout = NULL;
257 GString *buf = g_string_new("");
258 GString *js_ret = g_string_new("");
263 g_string_append_c(buf, *++s);
268 etype = get_exp_type(s);
273 vend = strpbrk(s, end_simple_var);
274 if(!vend) vend = strchr(s, '\0');
278 vend = strchr(s, upto);
279 if(!vend) vend = strchr(s, '\0');
283 strcpy(str_end, ")@");
285 vend = strstr(s, str_end);
286 if(!vend) vend = strchr(s, '\0');
290 strcpy(str_end, ">@");
292 vend = strstr(s, str_end);
293 if(!vend) vend = strchr(s, '\0');
297 strcpy(str_end, "]@");
299 vend = strstr(s, str_end);
300 if(!vend) vend = strchr(s, '\0');
305 strncpy(ret, s, vend-s);
309 if(etype == EXP_SIMPLE_VAR ||
310 etype == EXP_BRACED_VAR) {
311 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
312 if(c->type == TYPE_STR && *c->ptr != NULL) {
313 g_string_append(buf, (gchar *)*c->ptr);
314 } else if(c->type == TYPE_INT) {
315 g_string_append_printf(buf, "%d", (int)*c->ptr);
317 else if(c->type == TYPE_FLOAT) {
318 g_string_append_printf(buf, "%f", *(float *)c->ptr);
322 if(etype == EXP_SIMPLE_VAR)
327 else if(recurse != 1 &&
329 mycmd = expand(ret, 1);
330 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
334 g_printerr("error on running command: %s\n", err->message);
337 else if (*cmd_stdout) {
338 int len = strlen(cmd_stdout);
340 if(cmd_stdout[len-1] == '\n')
341 cmd_stdout[--len] = 0; /* strip trailing newline */
343 g_string_append(buf, cmd_stdout);
348 else if(recurse != 2 &&
350 mycmd = expand(ret, 2);
351 eval_js(uzbl.gui.web_view, mycmd, js_ret);
355 g_string_append(buf, js_ret->str);
356 g_string_free(js_ret, TRUE);
357 js_ret = g_string_new("");
361 else if(etype == EXP_ESCAPE) {
362 mycmd = expand(ret, 0);
363 char *escaped = g_markup_escape_text(mycmd, strlen(mycmd));
365 g_string_append(buf, escaped);
374 g_string_append_c(buf, *s);
379 g_string_free(js_ret, TRUE);
380 return g_string_free(buf, FALSE);
387 snprintf(tmp, sizeof(tmp), "%i", val);
388 return g_strdup(tmp);
392 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
395 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
398 str_replace (const char* search, const char* replace, const char* string) {
402 buf = g_strsplit (string, search, -1);
403 ret = g_strjoinv (replace, buf);
404 g_strfreev(buf); // somebody said this segfaults
410 read_file_by_line (gchar *path) {
411 GIOChannel *chan = NULL;
412 gchar *readbuf = NULL;
414 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
417 chan = g_io_channel_new_file(path, "r", NULL);
420 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
421 const gchar* val = g_strdup (readbuf);
422 g_array_append_val (lines, val);
427 g_io_channel_unref (chan);
429 fprintf(stderr, "File '%s' not be read.\n", path);
436 parseenv (char* string) {
437 extern char** environ;
438 gchar* tmpstr = NULL;
442 while (environ[i] != NULL) {
443 gchar** env = g_strsplit (environ[i], "=", 2);
444 gchar* envname = g_strconcat ("$", env[0], NULL);
446 if (g_strrstr (string, envname) != NULL) {
447 tmpstr = g_strdup(string);
449 string = str_replace(envname, env[1], tmpstr);
454 g_strfreev (env); // somebody said this breaks uzbl
462 setup_signal(int signr, sigfunc *shandler) {
463 struct sigaction nh, oh;
465 nh.sa_handler = shandler;
466 sigemptyset(&nh.sa_mask);
469 if(sigaction(signr, &nh, &oh) < 0)
477 if (uzbl.behave.fifo_dir)
478 unlink (uzbl.comm.fifo_path);
479 if (uzbl.behave.socket_dir)
480 unlink (uzbl.comm.socket_path);
482 g_free(uzbl.state.executable_path);
483 g_free(uzbl.state.keycmd);
484 g_hash_table_destroy(uzbl.bindings);
485 g_hash_table_destroy(uzbl.behave.commands);
488 /* used for html_mode_timeout
489 * be sure to extend this function to use
490 * more timers if needed in other places
493 set_timeout(int seconds) {
495 memset(&t, 0, sizeof t);
497 t.it_value.tv_sec = seconds;
498 t.it_value.tv_usec = 0;
499 setitimer(ITIMER_REAL, &t, NULL);
502 /* --- SIGNAL HANDLER --- */
505 catch_sigterm(int s) {
511 catch_sigint(int s) {
521 set_var_value("mode", "0");
526 /* --- CALLBACKS --- */
529 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
532 (void) navigation_action;
533 (void) policy_decision;
535 const gchar* uri = webkit_network_request_get_uri (request);
536 if (uzbl.state.verbose)
537 printf("New window requested -> %s \n", uri);
538 webkit_web_policy_decision_use(policy_decision);
543 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
548 /* If we can display it, let's display it... */
549 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
550 webkit_web_policy_decision_use (policy_decision);
554 /* ...everything we can't displayed is downloaded */
555 webkit_web_policy_decision_download (policy_decision);
560 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
564 if (uzbl.state.selected_url != NULL) {
565 if (uzbl.state.verbose)
566 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
567 new_window_load_uri(uzbl.state.selected_url);
569 if (uzbl.state.verbose)
570 printf("New web view -> %s\n","Nothing to open, exiting");
576 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
579 if (uzbl.behave.download_handler) {
580 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
581 if (uzbl.state.verbose)
582 printf("Download -> %s\n",uri);
583 /* if urls not escaped, we may have to escape and quote uri before this call */
584 run_handler(uzbl.behave.download_handler, uri);
589 /* scroll a bar in a given direction */
591 scroll (GtkAdjustment* bar, GArray *argv) {
595 gdouble page_size = gtk_adjustment_get_page_size(bar);
596 gdouble value = gtk_adjustment_get_value(bar);
597 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
600 value += page_size * amount * 0.01;
604 max_value = gtk_adjustment_get_upper(bar) - page_size;
606 if (value > max_value)
607 value = max_value; /* don't scroll past the end of the page */
609 gtk_adjustment_set_value (bar, value);
613 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
614 (void) page; (void) argv; (void) result;
615 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
619 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
620 (void) page; (void) argv; (void) result;
621 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
622 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
626 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
627 (void) page; (void) result;
628 scroll(uzbl.gui.bar_v, argv);
632 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
633 (void) page; (void) result;
634 scroll(uzbl.gui.bar_h, argv);
639 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
640 if(uzbl.state.verbose)
641 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
643 /* update geometry var with the actual geometry
644 this is necessary as some WMs don't seem to honour
645 the above setting and we don't want to end up with
646 wrong geometry information
653 if (!uzbl.behave.show_status) {
654 gtk_widget_hide(uzbl.gui.mainbar);
656 gtk_widget_show(uzbl.gui.mainbar);
662 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
667 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
671 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
676 if (uzbl.behave.show_status) {
677 gtk_widget_hide(uzbl.gui.mainbar);
679 gtk_widget_show(uzbl.gui.mainbar);
681 uzbl.behave.show_status = !uzbl.behave.show_status;
686 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
690 //Set selected_url state variable
691 g_free(uzbl.state.selected_url);
692 uzbl.state.selected_url = NULL;
694 uzbl.state.selected_url = g_strdup(link);
700 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
703 const gchar *title = webkit_web_view_get_title(web_view);
704 if (uzbl.gui.main_title)
705 g_free (uzbl.gui.main_title);
706 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
711 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
714 uzbl.gui.sbar.load_progress = progress;
716 g_free(uzbl.gui.sbar.progress_bar);
717 uzbl.gui.sbar.progress_bar = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
723 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
727 if (uzbl.behave.load_finish_handler)
728 run_handler(uzbl.behave.load_finish_handler, "");
731 void clear_keycmd() {
732 g_free(uzbl.state.keycmd);
733 uzbl.state.keycmd = g_strdup("");
737 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
741 uzbl.gui.sbar.load_progress = 0;
742 clear_keycmd(); // don't need old commands to remain on new page?
743 if (uzbl.behave.load_start_handler)
744 run_handler(uzbl.behave.load_start_handler, "");
748 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
751 g_free (uzbl.state.uri);
752 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
753 uzbl.state.uri = g_string_free (newuri, FALSE);
754 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
755 set_insert_mode(uzbl.behave.always_insert_mode);
758 if (uzbl.behave.load_commit_handler)
759 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
763 destroy_cb (GtkWidget* widget, gpointer data) {
771 if (uzbl.behave.history_handler) {
773 struct tm * timeinfo;
776 timeinfo = localtime ( &rawtime );
777 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
778 run_handler(uzbl.behave.history_handler, date);
783 /* VIEW funcs (little webkit wrappers) */
784 #define VIEWFUNC(name) void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
786 VIEWFUNC(reload_bypass_cache)
787 VIEWFUNC(stop_loading)
794 /* -- command to callback/function map for things we cannot attach to any signals */
795 struct {char *key; CommandInfo value;} cmdlist[] =
796 { /* key function no_split */
797 { "back", {view_go_back, 0} },
798 { "forward", {view_go_forward, 0} },
799 { "scroll_vert", {scroll_vert, 0} },
800 { "scroll_horz", {scroll_horz, 0} },
801 { "scroll_begin", {scroll_begin, 0} },
802 { "scroll_end", {scroll_end, 0} },
803 { "reload", {view_reload, 0}, },
804 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
805 { "stop", {view_stop_loading, 0}, },
806 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
807 { "zoom_out", {view_zoom_out, 0}, },
808 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
809 { "uri", {load_uri, TRUE} },
810 { "js", {run_js, TRUE} },
811 { "script", {run_external_js, 0} },
812 { "toggle_status", {toggle_status_cb, 0} },
813 { "spawn", {spawn, 0} },
814 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
815 { "sh", {spawn_sh, 0} },
816 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
817 { "exit", {close_uzbl, 0} },
818 { "search", {search_forward_text, TRUE} },
819 { "search_reverse", {search_reverse_text, TRUE} },
820 { "dehilight", {dehilight, 0} },
821 { "toggle_insert_mode", {toggle_insert_mode, 0} },
822 { "set", {set_var, TRUE} },
823 //{ "get", {get_var, TRUE} },
824 { "bind", {act_bind, TRUE} },
825 { "dump_config", {act_dump_config, 0} },
826 { "keycmd", {keycmd, TRUE} },
827 { "keycmd_nl", {keycmd_nl, TRUE} },
828 { "keycmd_bs", {keycmd_bs, 0} },
829 { "chain", {chain, 0} },
830 { "print", {print, TRUE} }
837 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
839 for (i = 0; i < LENGTH(cmdlist); i++)
840 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
843 /* -- CORE FUNCTIONS -- */
846 free_action(gpointer act) {
847 Action *action = (Action*)act;
848 g_free(action->name);
850 g_free(action->param);
855 new_action(const gchar *name, const gchar *param) {
856 Action *action = g_new(Action, 1);
858 action->name = g_strdup(name);
860 action->param = g_strdup(param);
862 action->param = NULL;
868 file_exists (const char * filename) {
869 return (access(filename, F_OK) == 0);
873 set_var(WebKitWebView *page, GArray *argv, GString *result) {
874 (void) page; (void) result;
875 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
876 if (split[0] != NULL) {
877 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
878 set_var_value(g_strstrip(split[0]), value);
885 print(WebKitWebView *page, GArray *argv, GString *result) {
886 (void) page; (void) result;
889 buf = expand(argv_idx(argv, 0), 0);
890 g_string_assign(result, buf);
895 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
896 (void) page; (void) result;
897 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
898 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
899 add_binding(g_strstrip(split[0]), value);
917 set_mode_indicator() {
918 uzbl.gui.sbar.mode_indicator = (uzbl.behave.insert_mode ?
919 uzbl.behave.insert_indicator : uzbl.behave.cmd_indicator);
924 set_mode_indicator();
929 set_insert_mode(gboolean mode) {
930 uzbl.behave.insert_mode = mode;
931 set_mode_indicator();
935 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
936 (void) page; (void) result;
938 if (argv_idx(argv, 0)) {
939 if (strcmp (argv_idx(argv, 0), "0") == 0) {
940 set_insert_mode(FALSE);
942 set_insert_mode(TRUE);
945 set_insert_mode( !uzbl.behave.insert_mode );
952 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
955 if (argv_idx(argv, 0)) {
956 GString* newuri = g_string_new (argv_idx(argv, 0));
957 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
958 run_js(web_view, argv, NULL);
961 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
962 g_string_prepend (newuri, "http://");
963 /* if we do handle cookies, ask our handler for them */
964 webkit_web_view_load_uri (web_view, newuri->str);
965 g_string_free (newuri, TRUE);
972 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
973 size_t argumentCount, const JSValueRef arguments[],
974 JSValueRef* exception) {
979 JSStringRef js_result_string;
980 GString *result = g_string_new("");
982 if (argumentCount >= 1) {
983 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
984 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
985 char ctl_line[arg_size];
986 JSStringGetUTF8CString(arg, ctl_line, arg_size);
988 parse_cmd_line(ctl_line, result);
990 JSStringRelease(arg);
992 js_result_string = JSStringCreateWithUTF8CString(result->str);
994 g_string_free(result, TRUE);
996 return JSValueMakeString(ctx, js_result_string);
999 JSStaticFunction js_static_functions[] = {
1000 {"run", js_run_command, kJSPropertyAttributeNone},
1005 /* This function creates the class and its definition, only once */
1006 if (!uzbl.js.initialized) {
1007 /* it would be pretty cool to make this dynamic */
1008 uzbl.js.classdef = kJSClassDefinitionEmpty;
1009 uzbl.js.classdef.staticFunctions = js_static_functions;
1011 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
1017 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
1018 WebKitWebFrame *frame;
1019 JSGlobalContextRef context;
1020 JSObjectRef globalobject;
1021 JSStringRef var_name;
1023 JSStringRef js_script;
1024 JSValueRef js_result;
1025 JSStringRef js_result_string;
1026 size_t js_result_size;
1030 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
1031 context = webkit_web_frame_get_global_context(frame);
1032 globalobject = JSContextGetGlobalObject(context);
1034 /* uzbl javascript namespace */
1035 var_name = JSStringCreateWithUTF8CString("Uzbl");
1036 JSObjectSetProperty(context, globalobject, var_name,
1037 JSObjectMake(context, uzbl.js.classref, NULL),
1038 kJSClassAttributeNone, NULL);
1040 /* evaluate the script and get return value*/
1041 js_script = JSStringCreateWithUTF8CString(script);
1042 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
1043 if (js_result && !JSValueIsUndefined(context, js_result)) {
1044 js_result_string = JSValueToStringCopy(context, js_result, NULL);
1045 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
1047 if (js_result_size) {
1048 char js_result_utf8[js_result_size];
1049 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
1050 g_string_assign(result, js_result_utf8);
1053 JSStringRelease(js_result_string);
1057 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
1059 JSStringRelease(var_name);
1060 JSStringRelease(js_script);
1064 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1065 if (argv_idx(argv, 0))
1066 eval_js(web_view, argv_idx(argv, 0), result);
1070 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
1072 if (argv_idx(argv, 0)) {
1073 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1078 while ((line = g_array_index(lines, gchar*, i))) {
1080 js = g_strdup (line);
1082 gchar* newjs = g_strconcat (js, line, NULL);
1089 if (uzbl.state.verbose)
1090 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1092 if (argv_idx (argv, 1)) {
1093 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1097 eval_js (web_view, js, result);
1099 g_array_free (lines, TRUE);
1104 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1105 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1106 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1107 webkit_web_view_unmark_text_matches (page);
1108 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1109 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1113 if (uzbl.state.searchtx) {
1114 if (uzbl.state.verbose)
1115 printf ("Searching: %s\n", uzbl.state.searchtx);
1116 webkit_web_view_set_highlight_text_matches (page, TRUE);
1117 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1122 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1124 search_text(page, argv, TRUE);
1128 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1130 search_text(page, argv, FALSE);
1134 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1135 (void) argv; (void) result;
1136 webkit_web_view_set_highlight_text_matches (page, FALSE);
1141 new_window_load_uri (const gchar * uri) {
1142 if (uzbl.behave.new_window) {
1143 GString *s = g_string_new ("");
1144 g_string_printf(s, "'%s'", uri);
1145 run_handler(uzbl.behave.new_window, s->str);
1148 GString* to_execute = g_string_new ("");
1149 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1151 for (i = 0; entries[i].long_name != NULL; i++) {
1152 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1153 gchar** str = (gchar**)entries[i].arg_data;
1155 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1159 if (uzbl.state.verbose)
1160 printf("\n%s\n", to_execute->str);
1161 g_spawn_command_line_async (to_execute->str, NULL);
1162 g_string_free (to_execute, TRUE);
1166 chain (WebKitWebView *page, GArray *argv, GString *result) {
1167 (void) page; (void) result;
1169 gchar **parts = NULL;
1171 while ((a = argv_idx(argv, i++))) {
1172 parts = g_strsplit (a, " ", 2);
1174 parse_command(parts[0], parts[1], result);
1180 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1184 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1190 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1194 uzbl.state.keycmd = g_strdup(argv_idx(argv, 0));
1200 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1205 int len = strlen(uzbl.state.keycmd);
1206 prev = g_utf8_find_prev_char(uzbl.state.keycmd, uzbl.state.keycmd + len);
1208 uzbl.state.keycmd[prev - uzbl.state.keycmd] = '\0';
1213 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1220 /* --Statusbar functions-- */
1222 build_progressbar_ascii(int percent) {
1223 int width=uzbl.gui.sbar.progress_w;
1226 GString *bar = g_string_new("");
1228 l = (double)percent*((double)width/100.);
1229 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1231 for(i=0; i<(int)l; i++)
1232 g_string_append(bar, uzbl.gui.sbar.progress_s);
1235 g_string_append(bar, uzbl.gui.sbar.progress_u);
1237 return g_string_free(bar, FALSE);
1239 /* --End Statusbar functions-- */
1242 sharg_append(GArray *a, const gchar *str) {
1243 const gchar *s = (str ? str : "");
1244 g_array_append_val(a, s);
1247 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1249 run_command (const gchar *command, const guint npre, const gchar **args,
1250 const gboolean sync, char **output_stdout) {
1251 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1254 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1255 gchar *pid = itos(getpid());
1256 gchar *xwin = itos(uzbl.xwin);
1258 sharg_append(a, command);
1259 for (i = 0; i < npre; i++) /* add n args before the default vars */
1260 sharg_append(a, args[i]);
1261 sharg_append(a, uzbl.state.config_file);
1262 sharg_append(a, pid);
1263 sharg_append(a, xwin);
1264 sharg_append(a, uzbl.comm.fifo_path);
1265 sharg_append(a, uzbl.comm.socket_path);
1266 sharg_append(a, uzbl.state.uri);
1267 sharg_append(a, uzbl.gui.main_title);
1269 for (i = npre; i < g_strv_length((gchar**)args); i++)
1270 sharg_append(a, args[i]);
1274 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1276 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1277 NULL, NULL, output_stdout, NULL, NULL, &err);
1278 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1279 NULL, NULL, NULL, &err);
1281 if (uzbl.state.verbose) {
1282 GString *s = g_string_new("spawned:");
1283 for (i = 0; i < (a->len); i++) {
1284 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1285 g_string_append_printf(s, " %s", qarg);
1288 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1289 printf("%s\n", s->str);
1290 g_string_free(s, TRUE);
1292 printf("Stdout: %s\n", *output_stdout);
1296 g_printerr("error on run_command: %s\n", err->message);
1301 g_array_free (a, TRUE);
1306 split_quoted(const gchar* src, const gboolean unquote) {
1307 /* split on unquoted space, return array of strings;
1308 remove a layer of quotes and backslashes if unquote */
1309 if (!src) return NULL;
1311 gboolean dq = FALSE;
1312 gboolean sq = FALSE;
1313 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1314 GString *s = g_string_new ("");
1318 for (p = src; *p != '\0'; p++) {
1319 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1320 else if (*p == '\\') { g_string_append_c(s, *p++);
1321 g_string_append_c(s, *p); }
1322 else if ((*p == '"') && unquote && !sq) dq = !dq;
1323 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1325 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1326 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1328 else if ((*p == ' ') && !dq && !sq) {
1329 dup = g_strdup(s->str);
1330 g_array_append_val(a, dup);
1331 g_string_truncate(s, 0);
1332 } else g_string_append_c(s, *p);
1334 dup = g_strdup(s->str);
1335 g_array_append_val(a, dup);
1336 ret = (gchar**)a->data;
1337 g_array_free (a, FALSE);
1338 g_string_free (s, TRUE);
1343 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1344 (void)web_view; (void)result;
1345 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1346 if (argv_idx(argv, 0))
1347 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1351 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1352 (void)web_view; (void)result;
1354 if (argv_idx(argv, 0))
1355 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1356 TRUE, &uzbl.comm.sync_stdout);
1360 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1361 (void)web_view; (void)result;
1362 if (!uzbl.behave.shell_cmd) {
1363 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1368 gchar *spacer = g_strdup("");
1369 g_array_insert_val(argv, 1, spacer);
1370 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1372 for (i = 1; i < g_strv_length(cmd); i++)
1373 g_array_prepend_val(argv, cmd[i]);
1375 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1381 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1382 (void)web_view; (void)result;
1383 if (!uzbl.behave.shell_cmd) {
1384 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1389 gchar *spacer = g_strdup("");
1390 g_array_insert_val(argv, 1, spacer);
1391 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1393 for (i = 1; i < g_strv_length(cmd); i++)
1394 g_array_prepend_val(argv, cmd[i]);
1396 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1397 TRUE, &uzbl.comm.sync_stdout);
1403 parse_command(const char *cmd, const char *param, GString *result) {
1406 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1408 gchar **par = split_quoted(param, TRUE);
1409 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1411 if (c->no_split) { /* don't split */
1412 sharg_append(a, param);
1414 for (i = 0; i < g_strv_length(par); i++)
1415 sharg_append(a, par[i]);
1418 if (result == NULL) {
1419 GString *result_print = g_string_new("");
1421 c->function(uzbl.gui.web_view, a, result_print);
1422 if (result_print->len)
1423 printf("%*s\n", result_print->len, result_print->str);
1425 g_string_free(result_print, TRUE);
1427 c->function(uzbl.gui.web_view, a, result);
1430 g_array_free (a, TRUE);
1433 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1440 if(*uzbl.net.proxy_url == ' '
1441 || uzbl.net.proxy_url == NULL) {
1442 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1443 (GType) SOUP_SESSION_PROXY_URI);
1446 suri = soup_uri_new(uzbl.net.proxy_url);
1447 g_object_set(G_OBJECT(uzbl.net.soup_session),
1448 SOUP_SESSION_PROXY_URI,
1450 soup_uri_free(suri);
1457 if(file_exists(uzbl.gui.icon)) {
1458 if (uzbl.gui.main_window)
1459 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1461 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1467 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1468 g_array_append_val (a, uzbl.state.uri);
1469 load_uri(uzbl.gui.web_view, a, NULL);
1470 g_array_free (a, TRUE);
1474 cmd_always_insert_mode() {
1475 set_insert_mode(uzbl.behave.always_insert_mode);
1481 g_object_set(G_OBJECT(uzbl.net.soup_session),
1482 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1486 cmd_max_conns_host() {
1487 g_object_set(G_OBJECT(uzbl.net.soup_session),
1488 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1493 soup_session_remove_feature
1494 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1495 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1496 /*g_free(uzbl.net.soup_logger);*/
1498 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1499 soup_session_add_feature(uzbl.net.soup_session,
1500 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1505 return webkit_web_view_get_settings(uzbl.gui.web_view);
1510 WebKitWebSettings *ws = view_settings();
1511 if (uzbl.behave.font_size > 0) {
1512 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1515 if (uzbl.behave.monospace_size > 0) {
1516 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1517 uzbl.behave.monospace_size, NULL);
1519 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1520 uzbl.behave.font_size, NULL);
1525 cmd_default_font_family() {
1526 g_object_set (G_OBJECT(view_settings()), "default-font-family",
1527 uzbl.behave.default_font_family, NULL);
1531 cmd_monospace_font_family() {
1532 g_object_set (G_OBJECT(view_settings()), "monospace-font-family",
1533 uzbl.behave.monospace_font_family, NULL);
1537 cmd_sans_serif_font_family() {
1538 g_object_set (G_OBJECT(view_settings()), "sans_serif-font-family",
1539 uzbl.behave.sans_serif_font_family, NULL);
1543 cmd_serif_font_family() {
1544 g_object_set (G_OBJECT(view_settings()), "serif-font-family",
1545 uzbl.behave.serif_font_family, NULL);
1549 cmd_cursive_font_family() {
1550 g_object_set (G_OBJECT(view_settings()), "cursive-font-family",
1551 uzbl.behave.cursive_font_family, NULL);
1555 cmd_fantasy_font_family() {
1556 g_object_set (G_OBJECT(view_settings()), "fantasy-font-family",
1557 uzbl.behave.fantasy_font_family, NULL);
1562 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1566 cmd_disable_plugins() {
1567 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1568 !uzbl.behave.disable_plugins, NULL);
1572 cmd_disable_scripts() {
1573 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1574 !uzbl.behave.disable_scripts, NULL);
1578 cmd_minimum_font_size() {
1579 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1580 uzbl.behave.minimum_font_size, NULL);
1583 cmd_autoload_img() {
1584 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1585 uzbl.behave.autoload_img, NULL);
1590 cmd_autoshrink_img() {
1591 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1592 uzbl.behave.autoshrink_img, NULL);
1597 cmd_enable_spellcheck() {
1598 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1599 uzbl.behave.enable_spellcheck, NULL);
1603 cmd_enable_private() {
1604 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1605 uzbl.behave.enable_private, NULL);
1610 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1611 uzbl.behave.print_bg, NULL);
1616 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1617 uzbl.behave.style_uri, NULL);
1621 cmd_resizable_txt() {
1622 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1623 uzbl.behave.resizable_txt, NULL);
1627 cmd_default_encoding() {
1628 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1629 uzbl.behave.default_encoding, NULL);
1633 cmd_enforce_96dpi() {
1634 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1635 uzbl.behave.enforce_96dpi, NULL);
1639 cmd_caret_browsing() {
1640 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1641 uzbl.behave.caret_browsing, NULL);
1645 cmd_cookie_handler() {
1646 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1647 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1648 if ((g_strcmp0(split[0], "sh") == 0) ||
1649 (g_strcmp0(split[0], "spawn") == 0)) {
1650 g_free (uzbl.behave.cookie_handler);
1651 uzbl.behave.cookie_handler =
1652 g_strdup_printf("sync_%s %s", split[0], split[1]);
1659 gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
1660 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1661 if ((g_strcmp0(split[0], "sh") == 0) ||
1662 (g_strcmp0(split[0], "spawn") == 0)) {
1663 g_free (uzbl.behave.new_window);
1664 uzbl.behave.new_window =
1665 g_strdup_printf("%s %s", split[0], split[1]);
1672 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1677 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1682 if(uzbl.behave.inject_html) {
1683 webkit_web_view_load_html_string (uzbl.gui.web_view,
1684 uzbl.behave.inject_html, NULL);
1693 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1694 uzbl.behave.modmask = 0;
1696 if(uzbl.behave.modkey)
1697 g_free(uzbl.behave.modkey);
1698 uzbl.behave.modkey = buf;
1700 for (i = 0; modkeys[i].key != NULL; i++) {
1701 if (g_strrstr(buf, modkeys[i].key))
1702 uzbl.behave.modmask |= modkeys[i].mask;
1708 if (*uzbl.net.useragent == ' ') {
1709 g_free (uzbl.net.useragent);
1710 uzbl.net.useragent = NULL;
1712 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT,
1713 uzbl.net.useragent, NULL);
1719 gtk_widget_ref(uzbl.gui.scrolled_win);
1720 gtk_widget_ref(uzbl.gui.mainbar);
1721 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1722 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1724 if(uzbl.behave.status_top) {
1725 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1726 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1729 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1730 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1732 gtk_widget_unref(uzbl.gui.scrolled_win);
1733 gtk_widget_unref(uzbl.gui.mainbar);
1734 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1739 set_var_value(gchar *name, gchar *val) {
1740 uzbl_cmdprop *c = NULL;
1744 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1745 if(!c->writeable) return FALSE;
1747 /* check for the variable type */
1748 if (c->type == TYPE_STR) {
1749 buf = expand(val, 0);
1752 } else if(c->type == TYPE_INT) {
1753 int *ip = (int *)c->ptr;
1754 buf = expand(val, 0);
1755 *ip = (int)strtoul(buf, &endp, 10);
1757 } else if (c->type == TYPE_FLOAT) {
1758 float *fp = (float *)c->ptr;
1759 buf = expand(val, 0);
1760 *fp = strtod(buf, &endp);
1764 /* invoke a command specific function */
1765 if(c->func) c->func();
1772 Behaviour *b = &uzbl.behave;
1774 if(b->html_buffer->str) {
1775 webkit_web_view_load_html_string (uzbl.gui.web_view,
1776 b->html_buffer->str, b->base_url);
1777 g_string_free(b->html_buffer, TRUE);
1778 b->html_buffer = g_string_new("");
1782 enum {M_CMD, M_HTML};
1784 parse_cmd_line(const char *ctl_line, GString *result) {
1785 Behaviour *b = &uzbl.behave;
1788 if(b->mode == M_HTML) {
1789 len = strlen(b->html_endmarker);
1790 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1791 if(len == strlen(ctl_line)-1 &&
1792 !strncmp(b->html_endmarker, ctl_line, len)) {
1794 set_var_value("mode", "0");
1799 set_timeout(b->html_timeout);
1800 g_string_append(b->html_buffer, ctl_line);
1803 else if((ctl_line[0] == '#') /* Comments */
1804 || (ctl_line[0] == ' ')
1805 || (ctl_line[0] == '\n'))
1806 ; /* ignore these lines */
1807 else { /* parse a command */
1809 gchar **tokens = NULL;
1810 len = strlen(ctl_line);
1812 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1813 ctlstrip = g_strndup(ctl_line, len - 1);
1814 else ctlstrip = g_strdup(ctl_line);
1816 tokens = g_strsplit(ctlstrip, " ", 2);
1817 parse_command(tokens[0], tokens[1], result);
1824 build_stream_name(int type, const gchar* dir) {
1825 State *s = &uzbl.state;
1829 str = g_strdup_printf
1830 ("%s/uzbl_fifo_%s", dir, s->instance_name);
1831 } else if (type == SOCKET) {
1832 str = g_strdup_printf
1833 ("%s/uzbl_socket_%s", dir, s->instance_name);
1839 control_fifo(GIOChannel *gio, GIOCondition condition) {
1840 if (uzbl.state.verbose)
1841 printf("triggered\n");
1846 if (condition & G_IO_HUP)
1847 g_error ("Fifo: Read end of pipe died!\n");
1850 g_error ("Fifo: GIOChannel broke\n");
1852 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1853 if (ret == G_IO_STATUS_ERROR) {
1854 g_error ("Fifo: Error reading: %s\n", err->message);
1858 parse_cmd_line(ctl_line, NULL);
1865 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1866 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1867 if (unlink(uzbl.comm.fifo_path) == -1)
1868 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1869 g_free(uzbl.comm.fifo_path);
1870 uzbl.comm.fifo_path = NULL;
1873 GIOChannel *chan = NULL;
1874 GError *error = NULL;
1875 gchar *path = build_stream_name(FIFO, dir);
1877 if (!file_exists(path)) {
1878 if (mkfifo (path, 0666) == 0) {
1879 // 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.
1880 chan = g_io_channel_new_file(path, "r+", &error);
1882 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1883 if (uzbl.state.verbose)
1884 printf ("init_fifo: created successfully as %s\n", path);
1885 uzbl.comm.fifo_path = path;
1887 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1888 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1889 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1890 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1892 /* if we got this far, there was an error; cleanup */
1893 if (error) g_error_free (error);
1900 control_stdin(GIOChannel *gio, GIOCondition condition) {
1902 gchar *ctl_line = NULL;
1905 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1906 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1909 parse_cmd_line(ctl_line, NULL);
1917 GIOChannel *chan = NULL;
1918 GError *error = NULL;
1920 chan = g_io_channel_unix_new(fileno(stdin));
1922 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1923 g_error ("Stdin: could not add watch\n");
1925 if (uzbl.state.verbose)
1926 printf ("Stdin: watch added successfully\n");
1929 g_error ("Stdin: Error while opening: %s\n", error->message);
1931 if (error) g_error_free (error);
1935 control_socket(GIOChannel *chan) {
1936 struct sockaddr_un remote;
1937 unsigned int t = sizeof(remote);
1939 GIOChannel *clientchan;
1941 clientsock = accept (g_io_channel_unix_get_fd(chan),
1942 (struct sockaddr *) &remote, &t);
1944 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1945 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1946 (GIOFunc) control_client_socket, clientchan);
1953 control_client_socket(GIOChannel *clientchan) {
1955 GString *result = g_string_new("");
1956 GError *error = NULL;
1960 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1961 if (ret == G_IO_STATUS_ERROR) {
1962 g_warning ("Error reading: %s\n", error->message);
1963 g_io_channel_shutdown(clientchan, TRUE, &error);
1965 } else if (ret == G_IO_STATUS_EOF) {
1966 /* shutdown and remove channel watch from main loop */
1967 g_io_channel_shutdown(clientchan, TRUE, &error);
1972 parse_cmd_line (ctl_line, result);
1973 g_string_append_c(result, '\n');
1974 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1976 if (ret == G_IO_STATUS_ERROR) {
1977 g_warning ("Error writing: %s", error->message);
1979 g_io_channel_flush(clientchan, &error);
1982 if (error) g_error_free (error);
1983 g_string_free(result, TRUE);
1989 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1990 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1991 if (unlink(uzbl.comm.socket_path) == -1)
1992 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1993 g_free(uzbl.comm.socket_path);
1994 uzbl.comm.socket_path = NULL;
2002 GIOChannel *chan = NULL;
2004 struct sockaddr_un local;
2005 gchar *path = build_stream_name(SOCKET, dir);
2007 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2009 local.sun_family = AF_UNIX;
2010 strcpy (local.sun_path, path);
2011 unlink (local.sun_path);
2013 len = strlen (local.sun_path) + sizeof (local.sun_family);
2014 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2015 if (uzbl.state.verbose)
2016 printf ("init_socket: opened in %s\n", path);
2019 if( (chan = g_io_channel_unix_new(sock)) ) {
2020 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2021 uzbl.comm.socket_path = path;
2024 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2026 /* if we got this far, there was an error; cleanup */
2033 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2034 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2036 // this function may be called very early when the templates are not set (yet), hence the checks
2038 update_title (void) {
2039 Behaviour *b = &uzbl.behave;
2042 if (b->show_status) {
2043 if (b->title_format_short) {
2044 parsed = expand(b->title_format_short, 0);
2045 if (uzbl.gui.main_window)
2046 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2049 if (b->status_format) {
2050 parsed = expand(b->status_format, 0);
2051 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2054 if (b->status_background) {
2056 gdk_color_parse (b->status_background, &color);
2057 //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)
2058 if (uzbl.gui.main_window)
2059 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2060 else if (uzbl.gui.plug)
2061 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2064 if (b->title_format_long) {
2065 parsed = expand(b->title_format_long, 0);
2066 if (uzbl.gui.main_window)
2067 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2074 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2078 retreive_geometry();
2083 key_press_cb (GtkWidget* window, GdkEventKey* event)
2085 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2089 if (event->type != GDK_KEY_PRESS ||
2090 event->keyval == GDK_Page_Up ||
2091 event->keyval == GDK_Page_Down ||
2092 event->keyval == GDK_Up ||
2093 event->keyval == GDK_Down ||
2094 event->keyval == GDK_Left ||
2095 event->keyval == GDK_Right ||
2096 event->keyval == GDK_Shift_L ||
2097 event->keyval == GDK_Shift_R)
2100 /* turn off insert mode (if always_insert_mode is not used) */
2101 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2102 set_insert_mode(uzbl.behave.always_insert_mode);
2107 if (uzbl.behave.insert_mode &&
2108 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2109 (!uzbl.behave.modmask)
2114 if (event->keyval == GDK_Escape) {
2117 dehilight(uzbl.gui.web_view, NULL, NULL);
2121 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2122 if (event->keyval == GDK_Insert) {
2124 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2125 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2127 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2130 GString* keycmd = g_string_new(uzbl.state.keycmd);
2131 g_string_append (keycmd, str);
2132 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2139 if (event->keyval == GDK_BackSpace)
2140 keycmd_bs(NULL, NULL, NULL);
2142 gboolean key_ret = FALSE;
2143 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2146 GString* keycmd = g_string_new(uzbl.state.keycmd);
2147 g_string_append(keycmd, event->string);
2148 uzbl.state.keycmd = g_string_free(keycmd, FALSE);
2151 run_keycmd(key_ret);
2153 if (key_ret) return (!uzbl.behave.insert_mode);
2158 run_keycmd(const gboolean key_ret) {
2159 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2161 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd))) {
2163 parse_command(act->name, act->param, NULL);
2167 /* try if it's an incremental keycmd or one that takes args, and run it */
2168 GString* short_keys = g_string_new ("");
2169 GString* short_keys_inc = g_string_new ("");
2171 guint len = strlen(uzbl.state.keycmd);
2172 for (i=0; i<len; i++) {
2173 g_string_append_c(short_keys, uzbl.state.keycmd[i]);
2174 g_string_assign(short_keys_inc, short_keys->str);
2175 g_string_append_c(short_keys, '_');
2176 g_string_append_c(short_keys_inc, '*');
2178 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2179 /* run normal cmds only if return was pressed */
2180 exec_paramcmd(act, i);
2183 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2184 if (key_ret) /* just quit the incremental command on return */
2186 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2190 g_string_truncate(short_keys, short_keys->len - 1);
2192 g_string_free (short_keys, TRUE);
2193 g_string_free (short_keys_inc, TRUE);
2197 exec_paramcmd(const Action *act, const guint i) {
2198 GString *parampart = g_string_new (uzbl.state.keycmd);
2199 GString *actionname = g_string_new ("");
2200 GString *actionparam = g_string_new ("");
2201 g_string_erase (parampart, 0, i+1);
2203 g_string_printf (actionname, act->name, parampart->str);
2205 g_string_printf (actionparam, act->param, parampart->str);
2206 parse_command(actionname->str, actionparam->str, NULL);
2207 g_string_free(actionname, TRUE);
2208 g_string_free(actionparam, TRUE);
2209 g_string_free(parampart, TRUE);
2217 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2218 //main_window_ref = g_object_ref(scrolled_window);
2219 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
2221 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2222 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2224 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2225 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2226 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2227 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2228 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2229 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2230 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2231 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2232 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2233 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2234 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2236 return scrolled_window;
2243 g->mainbar = gtk_hbox_new (FALSE, 0);
2245 /* keep a reference to the bar so we can re-pack it at runtime*/
2246 //sbar_ref = g_object_ref(g->mainbar);
2248 g->mainbar_label = gtk_label_new ("");
2249 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2250 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2251 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2252 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2253 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2254 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2260 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2261 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2262 gtk_widget_set_name (window, "Uzbl browser");
2263 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2264 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2265 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2272 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2273 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2274 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2281 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2283 If actname is one that calls an external command, this function will inject
2284 newargs in front of the user-provided args in that command line. They will
2285 come become after the body of the script (in sh) or after the name of
2286 the command to execute (in spawn).
2287 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2288 spawn <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2290 The return value consist of two strings: the action (sh, ...) and its args.
2292 If act is not one that calls an external command, then the given action merely
2295 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2296 /* Arrr! Here be memory leaks */
2297 gchar *actdup = g_strdup(actname);
2298 g_array_append_val(rets, actdup);
2300 if ((g_strcmp0(actname, "spawn") == 0) ||
2301 (g_strcmp0(actname, "sh") == 0) ||
2302 (g_strcmp0(actname, "sync_spawn") == 0) ||
2303 (g_strcmp0(actname, "sync_sh") == 0)) {
2305 GString *a = g_string_new("");
2306 gchar **spawnparts = split_quoted(origargs, FALSE);
2307 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2308 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2310 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2311 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2313 g_array_append_val(rets, a->str);
2314 g_string_free(a, FALSE);
2315 g_strfreev(spawnparts);
2317 gchar *origdup = g_strdup(origargs);
2318 g_array_append_val(rets, origdup);
2320 return (gchar**)g_array_free(rets, FALSE);
2324 run_handler (const gchar *act, const gchar *args) {
2325 /* Consider this code a temporary hack to make the handlers usable.
2326 In practice, all this splicing, injection, and reconstruction is
2327 inefficient, annoying and hard to manage. Potential pitfalls arise
2328 when the handler specific args 1) are not quoted (the handler
2329 callbacks should take care of this) 2) are quoted but interfere
2330 with the users' own quotation. A more ideal solution is
2331 to refactor parse_command so that it doesn't just take a string
2332 and execute it; rather than that, we should have a function which
2333 returns the argument vector parsed from the string. This vector
2334 could be modified (e.g. insert additional args into it) before
2335 passing it to the next function that actually executes it. Though
2336 it still isn't perfect for chain actions.. will reconsider & re-
2337 factor when I have the time. -duc */
2339 char **parts = g_strsplit(act, " ", 2);
2341 if (g_strcmp0(parts[0], "chain") == 0) {
2342 GString *newargs = g_string_new("");
2343 gchar **chainparts = split_quoted(parts[1], FALSE);
2345 /* for every argument in the chain, inject the handler args
2346 and make sure the new parts are wrapped in quotes */
2347 gchar **cp = chainparts;
2349 gchar *quotless = NULL;
2350 gchar **spliced_quotless = NULL; // sigh -_-;
2351 gchar **inpart = NULL;
2354 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2356 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2357 } else quotless = g_strdup(*cp);
2359 spliced_quotless = g_strsplit(quotless, " ", 2);
2360 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2361 g_strfreev(spliced_quotless);
2363 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2369 parse_command(parts[0], &(newargs->str[1]), NULL);
2370 g_string_free(newargs, TRUE);
2371 g_strfreev(chainparts);
2374 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2375 parse_command(inparts[0], inparts[1], NULL);
2383 add_binding (const gchar *key, const gchar *act) {
2384 char **parts = g_strsplit(act, " ", 2);
2391 if (uzbl.state.verbose)
2392 printf ("Binding %-10s : %s\n", key, act);
2393 action = new_action(parts[0], parts[1]);
2395 if (g_hash_table_remove (uzbl.bindings, key))
2396 g_warning ("Overwriting existing binding for \"%s\"", key);
2397 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2402 get_xdg_var (XDG_Var xdg) {
2403 const gchar* actual_value = getenv (xdg.environmental);
2404 const gchar* home = getenv ("HOME");
2405 gchar* return_value;
2407 if (! actual_value || strcmp (actual_value, "") == 0) {
2408 if (xdg.default_value) {
2409 return_value = str_replace ("~", home, xdg.default_value);
2411 return_value = NULL;
2414 return_value = str_replace("~", home, actual_value);
2417 return return_value;
2421 find_xdg_file (int xdg_type, char* filename) {
2422 /* xdg_type = 0 => config
2423 xdg_type = 1 => data
2424 xdg_type = 2 => cache*/
2426 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2427 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2430 gchar* temporary_string;
2434 if (! file_exists (temporary_file) && xdg_type != 2) {
2435 buf = get_xdg_var (XDG[3 + xdg_type]);
2436 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2439 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2440 g_free (temporary_file);
2441 temporary_file = g_strconcat (temporary_string, filename, NULL);
2445 //g_free (temporary_string); - segfaults.
2447 if (file_exists (temporary_file)) {
2448 return temporary_file;
2455 State *s = &uzbl.state;
2456 Network *n = &uzbl.net;
2458 for (i = 0; default_config[i].command != NULL; i++) {
2459 parse_cmd_line(default_config[i].command, NULL);
2462 if (g_strcmp0(s->config_file, "-") == 0) {
2463 s->config_file = NULL;
2467 else if (!s->config_file) {
2468 s->config_file = find_xdg_file (0, "/uzbl/config");
2471 if (s->config_file) {
2472 GArray* lines = read_file_by_line (s->config_file);
2476 while ((line = g_array_index(lines, gchar*, i))) {
2477 parse_cmd_line (line, NULL);
2481 g_array_free (lines, TRUE);
2483 if (uzbl.state.verbose)
2484 printf ("No configuration file loaded.\n");
2487 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2490 void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2493 if (!uzbl.behave.cookie_handler)
2496 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2497 GString *s = g_string_new ("");
2498 SoupURI * soup_uri = soup_message_get_uri(msg);
2499 g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
2500 run_handler(uzbl.behave.cookie_handler, s->str);
2502 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2503 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2504 if ( p != NULL ) *p = '\0';
2505 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2507 if (uzbl.comm.sync_stdout)
2508 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2510 g_string_free(s, TRUE);
2514 save_cookies (SoupMessage *msg, gpointer user_data){
2518 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2519 cookie = soup_cookie_to_set_cookie_header(ck->data);
2520 SoupURI * soup_uri = soup_message_get_uri(msg);
2521 GString *s = g_string_new ("");
2522 g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
2523 run_handler(uzbl.behave.cookie_handler, s->str);
2525 g_string_free(s, TRUE);
2530 /* --- WEBINSPECTOR --- */
2532 hide_window_cb(GtkWidget *widget, gpointer data) {
2535 gtk_widget_hide(widget);
2539 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2542 (void) web_inspector;
2543 GtkWidget* scrolled_window;
2544 GtkWidget* new_web_view;
2547 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2548 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2549 G_CALLBACK(hide_window_cb), NULL);
2551 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2552 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2553 gtk_widget_show(g->inspector_window);
2555 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2556 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2557 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2558 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2559 gtk_widget_show(scrolled_window);
2561 new_web_view = webkit_web_view_new();
2562 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2564 return WEBKIT_WEB_VIEW(new_web_view);
2568 inspector_show_window_cb (WebKitWebInspector* inspector){
2570 gtk_widget_show(uzbl.gui.inspector_window);
2574 /* TODO: Add variables and code to make use of these functions */
2576 inspector_close_window_cb (WebKitWebInspector* inspector){
2582 inspector_attach_window_cb (WebKitWebInspector* inspector){
2588 inspector_detach_window_cb (WebKitWebInspector* inspector){
2594 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2600 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2606 set_up_inspector() {
2608 WebKitWebSettings *settings = view_settings();
2609 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2611 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2612 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2613 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2614 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2615 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2616 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2617 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2619 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2623 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2625 uzbl_cmdprop *c = v;
2630 if(c->type == TYPE_STR)
2631 printf("set %s = %s\n", (char *)k, *c->ptr ? (char *)*c->ptr : " ");
2632 else if(c->type == TYPE_INT)
2633 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2634 else if(c->type == TYPE_FLOAT)
2635 printf("set %s = %f\n", (char *)k, *(float *)c->ptr);
2639 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2643 printf("bind %s = %s %s\n", (char *)k ,
2644 (char *)a->name, a->param?(char *)a->param:"");
2649 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2650 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2654 retreive_geometry() {
2656 GString *buf = g_string_new("");
2658 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2659 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2661 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2663 if(uzbl.gui.geometry)
2664 g_free(uzbl.gui.geometry);
2665 uzbl.gui.geometry = g_string_free(buf, FALSE);
2668 /* set up gtk, gobject, variable defaults and other things that tests and other
2669 * external applications need to do anyhow */
2671 initialize(int argc, char *argv[]) {
2672 gtk_init (&argc, &argv);
2673 if (!g_thread_supported ())
2674 g_thread_init (NULL);
2675 uzbl.state.executable_path = g_strdup(argv[0]);
2676 uzbl.state.selected_url = NULL;
2677 uzbl.state.searchtx = NULL;
2679 GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
2680 g_option_context_add_main_entries (context, entries, NULL);
2681 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2682 g_option_context_parse (context, &argc, &argv, NULL);
2683 g_option_context_free(context);
2685 if (uzbl.behave.print_version) {
2686 printf("Commit: %s\n", COMMIT);
2690 /* initialize hash table */
2691 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2693 uzbl.net.soup_session = webkit_get_default_session();
2694 uzbl.state.keycmd = g_strdup("");
2696 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2697 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2698 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2699 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2700 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2701 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2703 uzbl.gui.sbar.progress_s = g_strdup("="); //TODO: move these to config.h
2704 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2705 uzbl.gui.sbar.progress_w = 10;
2707 /* HTML mode defaults*/
2708 uzbl.behave.html_buffer = g_string_new("");
2709 uzbl.behave.html_endmarker = g_strdup(".");
2710 uzbl.behave.html_timeout = 60;
2711 uzbl.behave.base_url = g_strdup("http://invalid");
2713 /* default mode indicators */
2714 uzbl.behave.insert_indicator = g_strdup("I");
2715 uzbl.behave.cmd_indicator = g_strdup("C");
2717 uzbl.info.webkit_major = WEBKIT_MAJOR_VERSION;
2718 uzbl.info.webkit_minor = WEBKIT_MINOR_VERSION;
2719 uzbl.info.webkit_micro = WEBKIT_MICRO_VERSION;
2720 uzbl.info.arch = ARCH;
2721 uzbl.info.commit = COMMIT;
2724 make_var_to_name_hash();
2726 uzbl.gui.scrolled_win = create_browser();
2729 #ifndef UZBL_LIBRARY
2732 main (int argc, char* argv[]) {
2733 initialize(argc, argv);
2735 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2739 /* initial packing */
2740 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2741 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2743 if (uzbl.state.socket_id) {
2744 uzbl.gui.plug = create_plug ();
2745 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2746 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2748 uzbl.gui.main_window = create_window ();
2749 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2750 gtk_widget_show_all (uzbl.gui.main_window);
2751 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2754 if(!uzbl.state.instance_name)
2755 uzbl.state.instance_name = itos((int)uzbl.xwin);
2757 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2759 if (uzbl.state.verbose) {
2760 printf("Uzbl start location: %s\n", argv[0]);
2761 if (uzbl.state.socket_id)
2762 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2764 printf("window_id %i\n",(int) uzbl.xwin);
2765 printf("pid %i\n", getpid ());
2766 printf("name: %s\n", uzbl.state.instance_name);
2769 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2770 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2771 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2772 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2773 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2775 if(uzbl.gui.geometry)
2778 retreive_geometry();
2780 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2781 if (argc > 1 && !uzbl.state.uri)
2782 uri_override = g_strdup(argv[1]);
2783 gboolean verbose_override = uzbl.state.verbose;
2786 set_insert_mode(FALSE);
2788 if (!uzbl.behave.show_status)
2789 gtk_widget_hide(uzbl.gui.mainbar);
2796 if (verbose_override > uzbl.state.verbose)
2797 uzbl.state.verbose = verbose_override;
2800 set_var_value("uri", uri_override);
2801 g_free(uri_override);
2802 } else if (uzbl.state.uri)
2803 cmd_load_uri(uzbl.gui.web_view, NULL);
2808 return EXIT_SUCCESS;
2812 /* vi: set et ts=4: */