1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
163 { "SHIFT", GDK_SHIFT_MASK }, // shift
164 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
165 { "CONTROL", GDK_CONTROL_MASK }, // control
166 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
167 { "MOD2", GDK_MOD2_MASK }, // 5th mod
168 { "MOD3", GDK_MOD3_MASK }, // 6th mod
169 { "MOD4", GDK_MOD4_MASK }, // 7th mod
170 { "MOD5", GDK_MOD5_MASK }, // 8th mod
171 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
177 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
178 { "META", GDK_META_MASK }, // meta (since 2.10)
183 /* construct a hash from the var_name_to_ptr array for quick access */
185 make_var_to_name_hash() {
186 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
188 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
193 /* --- UTILITY FUNCTIONS --- */
195 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
197 get_exp_type(gchar *s) {
201 else if(*(s+1) == '{')
202 return EXP_BRACED_VAR;
203 else if(*(s+1) == '<')
206 return EXP_SIMPLE_VAR;
212 * recurse == 1: don't expand '@(command)@'
213 * recurse == 2: don't expand '@<java script>@'
216 expand(char *s, guint recurse) {
220 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
225 gchar *cmd_stdout = NULL;
227 GString *buf = g_string_new("");
228 GString *js_ret = g_string_new("");
233 g_string_append_c(buf, *++s);
238 etype = get_exp_type(s);
243 if( (vend = strpbrk(s, end_simple_var)) ||
244 (vend = strchr(s, '\0')) ) {
245 strncpy(ret, s, vend-s);
251 if( (vend = strchr(s, upto)) ||
252 (vend = strchr(s, '\0')) ) {
253 strncpy(ret, s, vend-s);
259 strcpy(str_end, ")@");
261 if( (vend = strstr(s, str_end)) ||
262 (vend = strchr(s, '\0')) ) {
263 strncpy(ret, s, vend-s);
269 strcpy(str_end, ">@");
271 if( (vend = strstr(s, str_end)) ||
272 (vend = strchr(s, '\0')) ) {
273 strncpy(ret, s, vend-s);
279 if(etype == EXP_SIMPLE_VAR ||
280 etype == EXP_BRACED_VAR) {
281 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
282 if(c->type == TYPE_STR)
283 g_string_append(buf, (gchar *)*c->ptr);
284 else if(c->type == TYPE_INT) {
285 char *b = itos((int)*c->ptr);
286 g_string_append(buf, b);
290 if(etype == EXP_SIMPLE_VAR)
295 else if(recurse != 1 &&
297 mycmd = expand(ret, 1);
298 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
302 g_printerr("error on running command: %s\n", err->message);
305 else if (*cmd_stdout) {
306 g_string_append(buf, cmd_stdout);
311 else if(recurse != 2 &&
313 mycmd = expand(ret, 2);
314 eval_js(uzbl.gui.web_view, mycmd, js_ret);
318 g_string_append(buf, js_ret->str);
319 g_string_free(js_ret, TRUE);
320 js_ret = g_string_new("");
327 g_string_append_c(buf, *s);
332 g_string_free(js_ret, TRUE);
333 return g_string_free(buf, FALSE);
340 snprintf(tmp, sizeof(tmp), "%i", val);
341 return g_strdup(tmp);
345 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
348 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
351 str_replace (const char* search, const char* replace, const char* string) {
355 buf = g_strsplit (string, search, -1);
356 ret = g_strjoinv (replace, buf);
357 g_strfreev(buf); // somebody said this segfaults
363 read_file_by_line (gchar *path) {
364 GIOChannel *chan = NULL;
365 gchar *readbuf = NULL;
367 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
370 chan = g_io_channel_new_file(path, "r", NULL);
373 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
374 const gchar* val = g_strdup (readbuf);
375 g_array_append_val (lines, val);
380 g_io_channel_unref (chan);
382 fprintf(stderr, "File '%s' not be read.\n", path);
389 gchar* parseenv (char* string) {
390 extern char** environ;
391 gchar* tmpstr = NULL;
395 while (environ[i] != NULL) {
396 gchar** env = g_strsplit (environ[i], "=", 2);
397 gchar* envname = g_strconcat ("$", env[0], NULL);
399 if (g_strrstr (string, envname) != NULL) {
400 tmpstr = g_strdup(string);
402 string = str_replace(envname, env[1], tmpstr);
407 g_strfreev (env); // somebody said this breaks uzbl
415 setup_signal(int signr, sigfunc *shandler) {
416 struct sigaction nh, oh;
418 nh.sa_handler = shandler;
419 sigemptyset(&nh.sa_mask);
422 if(sigaction(signr, &nh, &oh) < 0)
430 if (uzbl.behave.fifo_dir)
431 unlink (uzbl.comm.fifo_path);
432 if (uzbl.behave.socket_dir)
433 unlink (uzbl.comm.socket_path);
435 g_free(uzbl.state.executable_path);
436 g_string_free(uzbl.state.keycmd, TRUE);
437 g_hash_table_destroy(uzbl.bindings);
438 g_hash_table_destroy(uzbl.behave.commands);
441 /* used for html_mode_timeout
442 * be sure to extend this function to use
443 * more timers if needed in other places
446 set_timeout(int seconds) {
448 memset(&t, 0, sizeof t);
450 t.it_value.tv_sec = seconds;
451 t.it_value.tv_usec = 0;
452 setitimer(ITIMER_REAL, &t, NULL);
455 /* --- SIGNAL HANDLER --- */
458 catch_sigterm(int s) {
464 catch_sigint(int s) {
474 set_var_value("mode", "0");
479 /* --- CALLBACKS --- */
482 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
485 (void) navigation_action;
486 (void) policy_decision;
488 const gchar* uri = webkit_network_request_get_uri (request);
489 if (uzbl.state.verbose)
490 printf("New window requested -> %s \n", uri);
491 webkit_web_policy_decision_use(policy_decision);
496 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
501 /* If we can display it, let's display it... */
502 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
503 webkit_web_policy_decision_use (policy_decision);
507 /* ...everything we can't displayed is downloaded */
508 webkit_web_policy_decision_download (policy_decision);
513 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
517 if (uzbl.state.selected_url != NULL) {
518 if (uzbl.state.verbose)
519 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
520 new_window_load_uri(uzbl.state.selected_url);
522 if (uzbl.state.verbose)
523 printf("New web view -> %s\n","Nothing to open, exiting");
529 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
532 if (uzbl.behave.download_handler) {
533 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
534 if (uzbl.state.verbose)
535 printf("Download -> %s\n",uri);
536 /* if urls not escaped, we may have to escape and quote uri before this call */
537 run_handler(uzbl.behave.download_handler, uri);
542 /* scroll a bar in a given direction */
544 scroll (GtkAdjustment* bar, GArray *argv) {
548 gdouble page_size = gtk_adjustment_get_page_size(bar);
549 gdouble value = gtk_adjustment_get_value(bar);
550 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
553 value += page_size * amount * 0.01;
557 max_value = gtk_adjustment_get_upper(bar) - page_size;
559 if (value > max_value)
560 value = max_value; /* don't scroll past the end of the page */
562 gtk_adjustment_set_value (bar, value);
566 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
567 (void) page; (void) argv; (void) result;
568 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
572 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
573 (void) page; (void) argv; (void) result;
574 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
575 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
579 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
580 (void) page; (void) result;
581 scroll(uzbl.gui.bar_v, argv);
585 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
586 (void) page; (void) result;
587 scroll(uzbl.gui.bar_h, argv);
592 if (!uzbl.behave.show_status) {
593 gtk_widget_hide(uzbl.gui.mainbar);
595 gtk_widget_show(uzbl.gui.mainbar);
601 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
606 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
610 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
615 if (uzbl.behave.show_status) {
616 gtk_widget_hide(uzbl.gui.mainbar);
618 gtk_widget_show(uzbl.gui.mainbar);
620 uzbl.behave.show_status = !uzbl.behave.show_status;
625 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
629 //Set selected_url state variable
630 g_free(uzbl.state.selected_url);
631 uzbl.state.selected_url = NULL;
633 uzbl.state.selected_url = g_strdup(link);
639 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
642 const gchar *title = webkit_web_view_get_title(web_view);
643 if (uzbl.gui.main_title)
644 g_free (uzbl.gui.main_title);
645 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
650 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
653 uzbl.gui.sbar.load_progress = progress;
658 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
662 if (uzbl.behave.load_finish_handler)
663 run_handler(uzbl.behave.load_finish_handler, "");
667 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
671 uzbl.gui.sbar.load_progress = 0;
672 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
673 if (uzbl.behave.load_start_handler)
674 run_handler(uzbl.behave.load_start_handler, "");
678 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
681 g_free (uzbl.state.uri);
682 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
683 uzbl.state.uri = g_string_free (newuri, FALSE);
684 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
685 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
688 if (uzbl.behave.load_commit_handler)
689 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
693 destroy_cb (GtkWidget* widget, gpointer data) {
701 if (uzbl.behave.history_handler) {
703 struct tm * timeinfo;
706 timeinfo = localtime ( &rawtime );
707 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
708 run_handler(uzbl.behave.history_handler, date);
713 /* VIEW funcs (little webkit wrappers) */
714 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
716 VIEWFUNC(reload_bypass_cache)
717 VIEWFUNC(stop_loading)
724 /* -- command to callback/function map for things we cannot attach to any signals */
725 static struct {char *key; CommandInfo value;} cmdlist[] =
726 { /* key function no_split */
727 { "back", {view_go_back, 0} },
728 { "forward", {view_go_forward, 0} },
729 { "scroll_vert", {scroll_vert, 0} },
730 { "scroll_horz", {scroll_horz, 0} },
731 { "scroll_begin", {scroll_begin, 0} },
732 { "scroll_end", {scroll_end, 0} },
733 { "reload", {view_reload, 0}, },
734 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
735 { "stop", {view_stop_loading, 0}, },
736 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
737 { "zoom_out", {view_zoom_out, 0}, },
738 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
739 { "uri", {load_uri, TRUE} },
740 { "js", {run_js, TRUE} },
741 { "script", {run_external_js, 0} },
742 { "toggle_status", {toggle_status_cb, 0} },
743 { "spawn", {spawn, 0} },
744 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
745 { "sh", {spawn_sh, 0} },
746 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
747 { "exit", {close_uzbl, 0} },
748 { "search", {search_forward_text, TRUE} },
749 { "search_reverse", {search_reverse_text, TRUE} },
750 { "dehilight", {dehilight, 0} },
751 { "toggle_insert_mode", {toggle_insert_mode, 0} },
752 { "set", {set_var, TRUE} },
753 //{ "get", {get_var, TRUE} },
754 { "bind", {act_bind, TRUE} },
755 { "dump_config", {act_dump_config, 0} },
756 { "keycmd", {keycmd, TRUE} },
757 { "keycmd_nl", {keycmd_nl, TRUE} },
758 { "keycmd_bs", {keycmd_bs, 0} },
759 { "chain", {chain, 0} },
760 { "print", {print, TRUE} }
767 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
769 for (i = 0; i < LENGTH(cmdlist); i++)
770 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
773 /* -- CORE FUNCTIONS -- */
776 free_action(gpointer act) {
777 Action *action = (Action*)act;
778 g_free(action->name);
780 g_free(action->param);
785 new_action(const gchar *name, const gchar *param) {
786 Action *action = g_new(Action, 1);
788 action->name = g_strdup(name);
790 action->param = g_strdup(param);
792 action->param = NULL;
798 file_exists (const char * filename) {
799 return (access(filename, F_OK) == 0);
803 set_var(WebKitWebView *page, GArray *argv, GString *result) {
804 (void) page; (void) result;
805 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
806 if (split[0] != NULL) {
807 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
808 set_var_value(g_strstrip(split[0]), value);
815 print(WebKitWebView *page, GArray *argv, GString *result) {
816 (void) page; (void) result;
819 buf = expand(argv_idx(argv, 0), 0);
820 g_string_assign(result, buf);
825 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
826 (void) page; (void) result;
827 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
828 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
829 add_binding(g_strstrip(split[0]), value);
841 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
842 (void) page; (void) result;
844 if (argv_idx(argv, 0)) {
845 if (strcmp (argv_idx(argv, 0), "0") == 0) {
846 uzbl.behave.insert_mode = FALSE;
848 uzbl.behave.insert_mode = TRUE;
851 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
858 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
861 if (argv_idx(argv, 0)) {
862 GString* newuri = g_string_new (argv_idx(argv, 0));
863 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
864 run_js(web_view, argv, NULL);
867 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
868 g_string_prepend (newuri, "http://");
869 /* if we do handle cookies, ask our handler for them */
870 webkit_web_view_load_uri (web_view, newuri->str);
871 g_string_free (newuri, TRUE);
879 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
880 size_t argumentCount, const JSValueRef arguments[],
881 JSValueRef* exception) {
886 JSStringRef js_result_string;
887 GString *result = g_string_new("");
889 if (argumentCount >= 1) {
890 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
891 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
892 char ctl_line[arg_size];
893 JSStringGetUTF8CString(arg, ctl_line, arg_size);
895 parse_cmd_line(ctl_line, result);
897 JSStringRelease(arg);
899 js_result_string = JSStringCreateWithUTF8CString(result->str);
901 g_string_free(result, TRUE);
903 return JSValueMakeString(ctx, js_result_string);
906 static JSStaticFunction js_static_functions[] = {
907 {"run", js_run_command, kJSPropertyAttributeNone},
912 /* This function creates the class and its definition, only once */
913 if (!uzbl.js.initialized) {
914 /* it would be pretty cool to make this dynamic */
915 uzbl.js.classdef = kJSClassDefinitionEmpty;
916 uzbl.js.classdef.staticFunctions = js_static_functions;
918 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
924 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
925 WebKitWebFrame *frame;
926 JSGlobalContextRef context;
927 JSObjectRef globalobject;
928 JSStringRef var_name;
930 JSStringRef js_script;
931 JSValueRef js_result;
932 JSStringRef js_result_string;
933 size_t js_result_size;
937 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
938 context = webkit_web_frame_get_global_context(frame);
939 globalobject = JSContextGetGlobalObject(context);
941 /* uzbl javascript namespace */
942 var_name = JSStringCreateWithUTF8CString("Uzbl");
943 JSObjectSetProperty(context, globalobject, var_name,
944 JSObjectMake(context, uzbl.js.classref, NULL),
945 kJSClassAttributeNone, NULL);
947 /* evaluate the script and get return value*/
948 js_script = JSStringCreateWithUTF8CString(script);
949 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
950 if (js_result && !JSValueIsUndefined(context, js_result)) {
951 js_result_string = JSValueToStringCopy(context, js_result, NULL);
952 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
954 if (js_result_size) {
955 char js_result_utf8[js_result_size];
956 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
957 g_string_assign(result, js_result_utf8);
960 JSStringRelease(js_result_string);
964 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
966 JSStringRelease(var_name);
967 JSStringRelease(js_script);
971 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
973 if (argv_idx(argv, 0))
974 eval_js(web_view, argv_idx(argv, 0), result);
978 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
980 if (argv_idx(argv, 0)) {
981 GArray* lines = read_file_by_line (argv_idx (argv, 0));
986 while ((line = g_array_index(lines, gchar*, i))) {
988 js = g_strdup (line);
990 gchar* newjs = g_strconcat (js, line, NULL);
997 if (uzbl.state.verbose)
998 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1000 if (argv_idx (argv, 1)) {
1001 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1005 eval_js (web_view, js, result);
1007 g_array_free (lines, TRUE);
1012 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1013 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1014 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1015 webkit_web_view_unmark_text_matches (page);
1016 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1017 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1021 if (uzbl.state.searchtx) {
1022 if (uzbl.state.verbose)
1023 printf ("Searching: %s\n", uzbl.state.searchtx);
1024 webkit_web_view_set_highlight_text_matches (page, TRUE);
1025 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1030 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1032 search_text(page, argv, TRUE);
1036 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1038 search_text(page, argv, FALSE);
1042 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1043 (void) argv; (void) result;
1044 webkit_web_view_set_highlight_text_matches (page, FALSE);
1049 new_window_load_uri (const gchar * uri) {
1050 GString* to_execute = g_string_new ("");
1051 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1053 for (i = 0; entries[i].long_name != NULL; i++) {
1054 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1055 gchar** str = (gchar**)entries[i].arg_data;
1057 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1061 if (uzbl.state.verbose)
1062 printf("\n%s\n", to_execute->str);
1063 g_spawn_command_line_async (to_execute->str, NULL);
1064 g_string_free (to_execute, TRUE);
1068 chain (WebKitWebView *page, GArray *argv, GString *result) {
1069 (void) page; (void) result;
1071 gchar **parts = NULL;
1073 while ((a = argv_idx(argv, i++))) {
1074 parts = g_strsplit (a, " ", 2);
1075 parse_command(parts[0], parts[1], result);
1081 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1085 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1091 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1095 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1101 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1106 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1108 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1113 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1120 /* --Statusbar functions-- */
1122 build_progressbar_ascii(int percent) {
1123 int width=uzbl.gui.sbar.progress_w;
1126 GString *bar = g_string_new("");
1128 l = (double)percent*((double)width/100.);
1129 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1131 for(i=0; i<(int)l; i++)
1132 g_string_append(bar, uzbl.gui.sbar.progress_s);
1135 g_string_append(bar, uzbl.gui.sbar.progress_u);
1137 return g_string_free(bar, FALSE);
1142 const GScannerConfig scan_config = {
1145 ) /* cset_skip_characters */,
1150 ) /* cset_identifier_first */,
1157 ) /* cset_identifier_nth */,
1158 ( "" ) /* cpair_comment_single */,
1160 TRUE /* case_sensitive */,
1162 FALSE /* skip_comment_multi */,
1163 FALSE /* skip_comment_single */,
1164 FALSE /* scan_comment_multi */,
1165 TRUE /* scan_identifier */,
1166 TRUE /* scan_identifier_1char */,
1167 FALSE /* scan_identifier_NULL */,
1168 TRUE /* scan_symbols */,
1169 FALSE /* scan_binary */,
1170 FALSE /* scan_octal */,
1171 FALSE /* scan_float */,
1172 FALSE /* scan_hex */,
1173 FALSE /* scan_hex_dollar */,
1174 FALSE /* scan_string_sq */,
1175 FALSE /* scan_string_dq */,
1176 TRUE /* numbers_2_int */,
1177 FALSE /* int_2_float */,
1178 FALSE /* identifier_2_string */,
1179 FALSE /* char_2_token */,
1180 FALSE /* symbol_2_token */,
1181 TRUE /* scope_0_fallback */,
1186 uzbl.scan = g_scanner_new(&scan_config);
1187 while(symp->symbol_name) {
1188 g_scanner_scope_add_symbol(uzbl.scan, 0,
1190 GINT_TO_POINTER(symp->symbol_token));
1196 expand_template(const char *template, gboolean escape_markup) {
1197 if(!template) return NULL;
1199 GTokenType token = G_TOKEN_NONE;
1200 GString *ret = g_string_new("");
1204 g_scanner_input_text(uzbl.scan, template, strlen(template));
1205 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1206 token = g_scanner_get_next_token(uzbl.scan);
1208 if(token == G_TOKEN_SYMBOL) {
1209 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1213 buf = uzbl.state.uri?
1214 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1215 g_string_append(ret, buf);
1219 g_string_append(ret, uzbl.state.uri?
1220 uzbl.state.uri:g_strdup(""));
1223 buf = itos(uzbl.gui.sbar.load_progress);
1224 g_string_append(ret, buf);
1227 case SYM_LOADPRGSBAR:
1228 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1229 g_string_append(ret, buf);
1234 buf = uzbl.gui.main_title?
1235 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1236 g_string_append(ret, buf);
1240 g_string_append(ret, uzbl.gui.main_title?
1241 uzbl.gui.main_title:g_strdup(""));
1243 case SYM_SELECTED_URI:
1245 buf = uzbl.state.selected_url?
1246 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1247 g_string_append(ret, buf);
1251 g_string_append(ret, uzbl.state.selected_url?
1252 uzbl.state.selected_url:g_strdup(""));
1255 buf = itos(uzbl.xwin);
1256 g_string_append(ret,
1257 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1262 buf = uzbl.state.keycmd->str?
1263 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1264 g_string_append(ret, buf);
1268 g_string_append(ret, uzbl.state.keycmd->str?
1269 uzbl.state.keycmd->str:g_strdup(""));
1272 g_string_append(ret,
1273 uzbl.behave.insert_mode?
1274 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1277 g_string_append(ret,
1278 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1280 /* useragent syms */
1282 buf = itos(WEBKIT_MAJOR_VERSION);
1283 g_string_append(ret, buf);
1287 buf = itos(WEBKIT_MINOR_VERSION);
1288 g_string_append(ret, buf);
1292 buf = itos(WEBKIT_MICRO_VERSION);
1293 g_string_append(ret, buf);
1297 g_string_append(ret, uzbl.state.unameinfo.sysname);
1300 g_string_append(ret, uzbl.state.unameinfo.nodename);
1303 g_string_append(ret, uzbl.state.unameinfo.release);
1306 g_string_append(ret, uzbl.state.unameinfo.version);
1309 g_string_append(ret, uzbl.state.unameinfo.machine);
1312 g_string_append(ret, ARCH);
1315 case SYM_DOMAINNAME:
1316 g_string_append(ret, uzbl.state.unameinfo.domainname);
1320 g_string_append(ret, COMMIT);
1326 else if(token == G_TOKEN_INT) {
1327 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1328 g_string_append(ret, buf);
1331 else if(token == G_TOKEN_IDENTIFIER) {
1332 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1334 else if(token == G_TOKEN_CHAR) {
1335 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1339 return g_string_free(ret, FALSE);
1341 /* --End Statusbar functions-- */
1344 sharg_append(GArray *a, const gchar *str) {
1345 const gchar *s = (str ? str : "");
1346 g_array_append_val(a, s);
1349 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1351 run_command (const gchar *command, const guint npre, const gchar **args,
1352 const gboolean sync, char **output_stdout) {
1353 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1356 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1357 gchar *pid = itos(getpid());
1358 gchar *xwin = itos(uzbl.xwin);
1360 sharg_append(a, command);
1361 for (i = 0; i < npre; i++) /* add n args before the default vars */
1362 sharg_append(a, args[i]);
1363 sharg_append(a, uzbl.state.config_file);
1364 sharg_append(a, pid);
1365 sharg_append(a, xwin);
1366 sharg_append(a, uzbl.comm.fifo_path);
1367 sharg_append(a, uzbl.comm.socket_path);
1368 sharg_append(a, uzbl.state.uri);
1369 sharg_append(a, uzbl.gui.main_title);
1371 for (i = npre; i < g_strv_length((gchar**)args); i++)
1372 sharg_append(a, args[i]);
1376 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1378 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1379 NULL, NULL, output_stdout, NULL, NULL, &err);
1380 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1381 NULL, NULL, NULL, &err);
1383 if (uzbl.state.verbose) {
1384 GString *s = g_string_new("spawned:");
1385 for (i = 0; i < (a->len); i++) {
1386 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1387 g_string_append_printf(s, " %s", qarg);
1390 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1391 printf("%s\n", s->str);
1392 g_string_free(s, TRUE);
1394 printf("Stdout: %s\n", *output_stdout);
1398 g_printerr("error on run_command: %s\n", err->message);
1403 g_array_free (a, TRUE);
1408 split_quoted(const gchar* src, const gboolean unquote) {
1409 /* split on unquoted space, return array of strings;
1410 remove a layer of quotes and backslashes if unquote */
1411 if (!src) return NULL;
1413 gboolean dq = FALSE;
1414 gboolean sq = FALSE;
1415 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1416 GString *s = g_string_new ("");
1420 for (p = src; *p != '\0'; p++) {
1421 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1422 else if (*p == '\\') { g_string_append_c(s, *p++);
1423 g_string_append_c(s, *p); }
1424 else if ((*p == '"') && unquote && !sq) dq = !dq;
1425 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1427 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1428 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1430 else if ((*p == ' ') && !dq && !sq) {
1431 dup = g_strdup(s->str);
1432 g_array_append_val(a, dup);
1433 g_string_truncate(s, 0);
1434 } else g_string_append_c(s, *p);
1436 dup = g_strdup(s->str);
1437 g_array_append_val(a, dup);
1438 ret = (gchar**)a->data;
1439 g_array_free (a, FALSE);
1440 g_string_free (s, TRUE);
1445 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1446 (void)web_view; (void)result;
1447 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1448 if (argv_idx(argv, 0))
1449 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1453 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1454 (void)web_view; (void)result;
1456 if (argv_idx(argv, 0))
1457 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1458 TRUE, &uzbl.comm.sync_stdout);
1462 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1463 (void)web_view; (void)result;
1464 if (!uzbl.behave.shell_cmd) {
1465 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1470 gchar *spacer = g_strdup("");
1471 g_array_insert_val(argv, 1, spacer);
1472 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1474 for (i = 1; i < g_strv_length(cmd); i++)
1475 g_array_prepend_val(argv, cmd[i]);
1477 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1483 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1484 (void)web_view; (void)result;
1485 if (!uzbl.behave.shell_cmd) {
1486 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1491 gchar *spacer = g_strdup("");
1492 g_array_insert_val(argv, 1, spacer);
1493 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1495 for (i = 1; i < g_strv_length(cmd); i++)
1496 g_array_prepend_val(argv, cmd[i]);
1498 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1499 TRUE, &uzbl.comm.sync_stdout);
1505 parse_command(const char *cmd, const char *param, GString *result) {
1508 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1510 gchar **par = split_quoted(param, TRUE);
1511 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1513 if (c->no_split) { /* don't split */
1514 sharg_append(a, param);
1516 for (i = 0; i < g_strv_length(par); i++)
1517 sharg_append(a, par[i]);
1520 if (result == NULL) {
1521 GString *result_print = g_string_new("");
1523 c->function(uzbl.gui.web_view, a, result_print);
1524 if (result_print->len)
1525 printf("%*s\n", result_print->len, result_print->str);
1527 g_string_free(result_print, TRUE);
1529 c->function(uzbl.gui.web_view, a, result);
1532 g_array_free (a, TRUE);
1535 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1542 if(*uzbl.net.proxy_url == ' '
1543 || uzbl.net.proxy_url == NULL) {
1544 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1545 (GType) SOUP_SESSION_PROXY_URI);
1548 suri = soup_uri_new(uzbl.net.proxy_url);
1549 g_object_set(G_OBJECT(uzbl.net.soup_session),
1550 SOUP_SESSION_PROXY_URI,
1552 soup_uri_free(suri);
1559 if(file_exists(uzbl.gui.icon)) {
1560 if (uzbl.gui.main_window)
1561 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1563 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1569 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1570 g_array_append_val (a, uzbl.state.uri);
1571 load_uri(uzbl.gui.web_view, a, NULL);
1572 g_array_free (a, TRUE);
1576 cmd_always_insert_mode() {
1577 uzbl.behave.insert_mode =
1578 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1584 g_object_set(G_OBJECT(uzbl.net.soup_session),
1585 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1589 cmd_max_conns_host() {
1590 g_object_set(G_OBJECT(uzbl.net.soup_session),
1591 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1596 soup_session_remove_feature
1597 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1598 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1599 /*g_free(uzbl.net.soup_logger);*/
1601 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1602 soup_session_add_feature(uzbl.net.soup_session,
1603 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1606 static WebKitWebSettings*
1608 return webkit_web_view_get_settings(uzbl.gui.web_view);
1613 WebKitWebSettings *ws = view_settings();
1614 if (uzbl.behave.font_size > 0) {
1615 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1618 if (uzbl.behave.monospace_size > 0) {
1619 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1620 uzbl.behave.monospace_size, NULL);
1622 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1623 uzbl.behave.font_size, NULL);
1629 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1633 cmd_disable_plugins() {
1634 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1635 !uzbl.behave.disable_plugins, NULL);
1639 cmd_disable_scripts() {
1640 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1641 !uzbl.behave.disable_scripts, NULL);
1645 cmd_minimum_font_size() {
1646 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1647 uzbl.behave.minimum_font_size, NULL);
1650 cmd_autoload_img() {
1651 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1652 uzbl.behave.autoload_img, NULL);
1657 cmd_autoshrink_img() {
1658 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1659 uzbl.behave.autoshrink_img, NULL);
1664 cmd_enable_spellcheck() {
1665 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1666 uzbl.behave.enable_spellcheck, NULL);
1670 cmd_enable_private() {
1671 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1672 uzbl.behave.enable_private, NULL);
1677 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1678 uzbl.behave.print_bg, NULL);
1683 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1684 uzbl.behave.style_uri, NULL);
1688 cmd_resizable_txt() {
1689 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1690 uzbl.behave.resizable_txt, NULL);
1694 cmd_default_encoding() {
1695 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1696 uzbl.behave.default_encoding, NULL);
1700 cmd_enforce_96dpi() {
1701 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1702 uzbl.behave.enforce_96dpi, NULL);
1706 cmd_caret_browsing() {
1707 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1708 uzbl.behave.caret_browsing, NULL);
1712 cmd_cookie_handler() {
1713 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1714 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1715 if ((g_strcmp0(split[0], "sh") == 0) ||
1716 (g_strcmp0(split[0], "spawn") == 0)) {
1717 g_free (uzbl.behave.cookie_handler);
1718 uzbl.behave.cookie_handler =
1719 g_strdup_printf("sync_%s %s", split[0], split[1]);
1726 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1731 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1736 if(uzbl.behave.inject_html) {
1737 webkit_web_view_load_html_string (uzbl.gui.web_view,
1738 uzbl.behave.inject_html, NULL);
1747 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1748 uzbl.behave.modmask = 0;
1750 if(uzbl.behave.modkey)
1751 g_free(uzbl.behave.modkey);
1752 uzbl.behave.modkey = buf;
1754 for (i = 0; modkeys[i].key != NULL; i++) {
1755 if (g_strrstr(buf, modkeys[i].key))
1756 uzbl.behave.modmask |= modkeys[i].mask;
1762 if (*uzbl.net.useragent == ' ') {
1763 g_free (uzbl.net.useragent);
1764 uzbl.net.useragent = NULL;
1766 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1768 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1769 g_free(uzbl.net.useragent);
1770 uzbl.net.useragent = ua;
1776 gtk_widget_ref(uzbl.gui.scrolled_win);
1777 gtk_widget_ref(uzbl.gui.mainbar);
1778 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1779 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1781 if(uzbl.behave.status_top) {
1782 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1783 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1786 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1787 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1789 gtk_widget_unref(uzbl.gui.scrolled_win);
1790 gtk_widget_unref(uzbl.gui.mainbar);
1791 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1796 set_var_value(gchar *name, gchar *val) {
1797 uzbl_cmdprop *c = NULL;
1801 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1802 /* check for the variable type */
1803 if (c->type == TYPE_STR) {
1804 buf = expand(val, 0);
1807 } else if(c->type == TYPE_INT) {
1808 int *ip = (int *)c->ptr;
1809 buf = expand(val, 0);
1810 *ip = (int)strtoul(buf, &endp, 10);
1812 } else if (c->type == TYPE_FLOAT) {
1813 float *fp = (float *)c->ptr;
1814 buf = expand(val, 0);
1815 *fp = strtod(buf, &endp);
1819 /* invoke a command specific function */
1820 if(c->func) c->func();
1827 Behaviour *b = &uzbl.behave;
1829 if(b->html_buffer->str) {
1830 webkit_web_view_load_html_string (uzbl.gui.web_view,
1831 b->html_buffer->str, b->base_url);
1832 g_string_free(b->html_buffer, TRUE);
1833 b->html_buffer = g_string_new("");
1837 enum {M_CMD, M_HTML};
1839 parse_cmd_line(const char *ctl_line, GString *result) {
1840 Behaviour *b = &uzbl.behave;
1843 if(b->mode == M_HTML) {
1844 len = strlen(b->html_endmarker);
1845 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1846 if(len == strlen(ctl_line)-1 &&
1847 !strncmp(b->html_endmarker, ctl_line, len)) {
1849 set_var_value("mode", "0");
1854 set_timeout(b->html_timeout);
1855 g_string_append(b->html_buffer, ctl_line);
1858 else if((ctl_line[0] == '#') /* Comments */
1859 || (ctl_line[0] == ' ')
1860 || (ctl_line[0] == '\n'))
1861 ; /* ignore these lines */
1862 else { /* parse a command */
1864 gchar **tokens = NULL;
1865 len = strlen(ctl_line);
1867 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1868 ctlstrip = g_strndup(ctl_line, len - 1);
1869 else ctlstrip = g_strdup(ctl_line);
1871 tokens = g_strsplit(ctlstrip, " ", 2);
1872 parse_command(tokens[0], tokens[1], result);
1879 build_stream_name(int type, const gchar* dir) {
1880 char *xwin_str = NULL;
1881 State *s = &uzbl.state;
1884 xwin_str = itos((int)uzbl.xwin);
1886 str = g_strdup_printf
1887 ("%s/uzbl_fifo_%s", dir,
1888 s->instance_name ? s->instance_name : xwin_str);
1889 } else if (type == SOCKET) {
1890 str = g_strdup_printf
1891 ("%s/uzbl_socket_%s", dir,
1892 s->instance_name ? s->instance_name : xwin_str );
1899 control_fifo(GIOChannel *gio, GIOCondition condition) {
1900 if (uzbl.state.verbose)
1901 printf("triggered\n");
1906 if (condition & G_IO_HUP)
1907 g_error ("Fifo: Read end of pipe died!\n");
1910 g_error ("Fifo: GIOChannel broke\n");
1912 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1913 if (ret == G_IO_STATUS_ERROR) {
1914 g_error ("Fifo: Error reading: %s\n", err->message);
1918 parse_cmd_line(ctl_line, NULL);
1925 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1926 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1927 if (unlink(uzbl.comm.fifo_path) == -1)
1928 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1929 g_free(uzbl.comm.fifo_path);
1930 uzbl.comm.fifo_path = NULL;
1933 if (*dir == ' ') { /* space unsets the variable */
1938 GIOChannel *chan = NULL;
1939 GError *error = NULL;
1940 gchar *path = build_stream_name(FIFO, dir);
1942 if (!file_exists(path)) {
1943 if (mkfifo (path, 0666) == 0) {
1944 // 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.
1945 chan = g_io_channel_new_file(path, "r+", &error);
1947 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1948 if (uzbl.state.verbose)
1949 printf ("init_fifo: created successfully as %s\n", path);
1950 uzbl.comm.fifo_path = path;
1952 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1953 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1954 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1955 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1957 /* if we got this far, there was an error; cleanup */
1958 if (error) g_error_free (error);
1965 control_stdin(GIOChannel *gio, GIOCondition condition) {
1967 gchar *ctl_line = NULL;
1970 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1971 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1974 parse_cmd_line(ctl_line, NULL);
1982 GIOChannel *chan = NULL;
1983 GError *error = NULL;
1985 chan = g_io_channel_unix_new(fileno(stdin));
1987 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1988 g_error ("Stdin: could not add watch\n");
1990 if (uzbl.state.verbose)
1991 printf ("Stdin: watch added successfully\n");
1994 g_error ("Stdin: Error while opening: %s\n", error->message);
1996 if (error) g_error_free (error);
2000 control_socket(GIOChannel *chan) {
2001 struct sockaddr_un remote;
2002 unsigned int t = sizeof(remote);
2004 GIOChannel *clientchan;
2006 clientsock = accept (g_io_channel_unix_get_fd(chan),
2007 (struct sockaddr *) &remote, &t);
2009 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2010 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2011 (GIOFunc) control_client_socket, clientchan);
2018 control_client_socket(GIOChannel *clientchan) {
2020 GString *result = g_string_new("");
2021 GError *error = NULL;
2025 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2026 if (ret == G_IO_STATUS_ERROR) {
2027 g_warning ("Error reading: %s\n", error->message);
2028 g_io_channel_shutdown(clientchan, TRUE, &error);
2030 } else if (ret == G_IO_STATUS_EOF) {
2031 /* shutdown and remove channel watch from main loop */
2032 g_io_channel_shutdown(clientchan, TRUE, &error);
2037 parse_cmd_line (ctl_line, result);
2038 g_string_append_c(result, '\n');
2039 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2041 if (ret == G_IO_STATUS_ERROR) {
2042 g_warning ("Error writing: %s", error->message);
2044 g_io_channel_flush(clientchan, &error);
2047 if (error) g_error_free (error);
2048 g_string_free(result, TRUE);
2054 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2055 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2056 if (unlink(uzbl.comm.socket_path) == -1)
2057 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2058 g_free(uzbl.comm.socket_path);
2059 uzbl.comm.socket_path = NULL;
2067 GIOChannel *chan = NULL;
2069 struct sockaddr_un local;
2070 gchar *path = build_stream_name(SOCKET, dir);
2072 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2074 local.sun_family = AF_UNIX;
2075 strcpy (local.sun_path, path);
2076 unlink (local.sun_path);
2078 len = strlen (local.sun_path) + sizeof (local.sun_family);
2079 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2080 if (uzbl.state.verbose)
2081 printf ("init_socket: opened in %s\n", path);
2084 if( (chan = g_io_channel_unix_new(sock)) ) {
2085 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2086 uzbl.comm.socket_path = path;
2089 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2091 /* if we got this far, there was an error; cleanup */
2098 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2099 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2101 // this function may be called very early when the templates are not set (yet), hence the checks
2103 update_title (void) {
2104 Behaviour *b = &uzbl.behave;
2107 if (b->show_status) {
2108 if (b->title_format_short) {
2109 parsed = expand_template(b->title_format_short, FALSE);
2110 if (uzbl.gui.main_window)
2111 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2114 if (b->status_format) {
2115 parsed = expand_template(b->status_format, TRUE);
2116 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2119 if (b->status_background) {
2121 gdk_color_parse (b->status_background, &color);
2122 //labels and hboxes do not draw their own background. applying this on the vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2123 if (uzbl.gui.main_window)
2124 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2125 else if (uzbl.gui.plug)
2126 gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
2129 if (b->title_format_long) {
2130 parsed = expand_template(b->title_format_long, FALSE);
2131 if (uzbl.gui.main_window)
2132 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2139 key_press_cb (GtkWidget* window, GdkEventKey* event)
2141 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2145 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2146 || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right || event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R)
2149 /* turn off insert mode (if always_insert_mode is not used) */
2150 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2151 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2156 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2159 if (event->keyval == GDK_Escape) {
2160 g_string_truncate(uzbl.state.keycmd, 0);
2162 dehilight(uzbl.gui.web_view, NULL, NULL);
2166 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2167 if (event->keyval == GDK_Insert) {
2169 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2170 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2172 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2175 g_string_append (uzbl.state.keycmd, str);
2182 if (event->keyval == GDK_BackSpace)
2183 keycmd_bs(NULL, NULL, NULL);
2185 gboolean key_ret = FALSE;
2186 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2188 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2190 run_keycmd(key_ret);
2192 if (key_ret) return (!uzbl.behave.insert_mode);
2197 run_keycmd(const gboolean key_ret) {
2198 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2200 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2201 g_string_truncate(uzbl.state.keycmd, 0);
2202 parse_command(act->name, act->param, NULL);
2206 /* try if it's an incremental keycmd or one that takes args, and run it */
2207 GString* short_keys = g_string_new ("");
2208 GString* short_keys_inc = g_string_new ("");
2210 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2211 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2212 g_string_assign(short_keys_inc, short_keys->str);
2213 g_string_append_c(short_keys, '_');
2214 g_string_append_c(short_keys_inc, '*');
2216 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2217 /* run normal cmds only if return was pressed */
2218 exec_paramcmd(act, i);
2219 g_string_truncate(uzbl.state.keycmd, 0);
2221 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2222 if (key_ret) /* just quit the incremental command on return */
2223 g_string_truncate(uzbl.state.keycmd, 0);
2224 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2228 g_string_truncate(short_keys, short_keys->len - 1);
2230 g_string_free (short_keys, TRUE);
2231 g_string_free (short_keys_inc, TRUE);
2235 exec_paramcmd(const Action *act, const guint i) {
2236 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2237 GString *actionname = g_string_new ("");
2238 GString *actionparam = g_string_new ("");
2239 g_string_erase (parampart, 0, i+1);
2241 g_string_printf (actionname, act->name, parampart->str);
2243 g_string_printf (actionparam, act->param, parampart->str);
2244 parse_command(actionname->str, actionparam->str, NULL);
2245 g_string_free(actionname, TRUE);
2246 g_string_free(actionparam, TRUE);
2247 g_string_free(parampart, TRUE);
2255 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2256 //main_window_ref = g_object_ref(scrolled_window);
2257 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
2259 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2260 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2262 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2263 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2268 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2269 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2270 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2271 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2272 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2274 return scrolled_window;
2281 g->mainbar = gtk_hbox_new (FALSE, 0);
2283 /* keep a reference to the bar so we can re-pack it at runtime*/
2284 //sbar_ref = g_object_ref(g->mainbar);
2286 g->mainbar_label = gtk_label_new ("");
2287 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2288 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2289 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2290 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2291 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2292 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2297 GtkWidget* create_window () {
2298 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2299 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2300 gtk_widget_set_name (window, "Uzbl browser");
2301 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2302 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2308 GtkPlug* create_plug () {
2309 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2310 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2311 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2318 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2320 If actname is one that calls an external command, this function will inject
2321 newargs in front of the user-provided args in that command line. They will
2322 come become after the body of the script (in sh) or after the name of
2323 the command to execute (in spawn).
2324 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2325 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2327 The return value consist of two strings: the action (sh, ...) and its args.
2329 If act is not one that calls an external command, then the given action merely
2332 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2333 gchar *actdup = g_strdup(actname);
2334 g_array_append_val(rets, actdup);
2336 if ((g_strcmp0(actname, "spawn") == 0) ||
2337 (g_strcmp0(actname, "sh") == 0) ||
2338 (g_strcmp0(actname, "sync_spawn") == 0) ||
2339 (g_strcmp0(actname, "sync_sh") == 0)) {
2341 GString *a = g_string_new("");
2342 gchar **spawnparts = split_quoted(origargs, FALSE);
2343 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2344 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2346 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2347 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2349 g_array_append_val(rets, a->str);
2350 g_string_free(a, FALSE);
2351 g_strfreev(spawnparts);
2353 gchar *origdup = g_strdup(origargs);
2354 g_array_append_val(rets, origdup);
2356 return (gchar**)g_array_free(rets, FALSE);
2360 run_handler (const gchar *act, const gchar *args) {
2361 /* Consider this code a temporary hack to make the handlers usable.
2362 In practice, all this splicing, injection, and reconstruction is
2363 inefficient, annoying and hard to manage. Potential pitfalls arise
2364 when the handler specific args 1) are not quoted (the handler
2365 callbacks should take care of this) 2) are quoted but interfere
2366 with the users' own quotation. A more ideal solution is
2367 to refactor parse_command so that it doesn't just take a string
2368 and execute it; rather than that, we should have a function which
2369 returns the argument vector parsed from the string. This vector
2370 could be modified (e.g. insert additional args into it) before
2371 passing it to the next function that actually executes it. Though
2372 it still isn't perfect for chain actions.. will reconsider & re-
2373 factor when I have the time. -duc */
2375 char **parts = g_strsplit(act, " ", 2);
2377 if (g_strcmp0(parts[0], "chain") == 0) {
2378 GString *newargs = g_string_new("");
2379 gchar **chainparts = split_quoted(parts[1], FALSE);
2381 /* for every argument in the chain, inject the handler args
2382 and make sure the new parts are wrapped in quotes */
2383 gchar **cp = chainparts;
2385 gchar *quotless = NULL;
2386 gchar **spliced_quotless = NULL; // sigh -_-;
2387 gchar **inpart = NULL;
2390 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2392 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2393 } else quotless = g_strdup(*cp);
2395 spliced_quotless = g_strsplit(quotless, " ", 2);
2396 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2397 g_strfreev(spliced_quotless);
2399 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2405 parse_command(parts[0], &(newargs->str[1]), NULL);
2406 g_string_free(newargs, TRUE);
2407 g_strfreev(chainparts);
2410 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2411 parse_command(inparts[0], inparts[1], NULL);
2419 add_binding (const gchar *key, const gchar *act) {
2420 char **parts = g_strsplit(act, " ", 2);
2427 if (uzbl.state.verbose)
2428 printf ("Binding %-10s : %s\n", key, act);
2429 action = new_action(parts[0], parts[1]);
2431 if (g_hash_table_remove (uzbl.bindings, key))
2432 g_warning ("Overwriting existing binding for \"%s\"", key);
2433 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2438 get_xdg_var (XDG_Var xdg) {
2439 const gchar* actual_value = getenv (xdg.environmental);
2440 const gchar* home = getenv ("HOME");
2441 gchar* return_value;
2443 if (! actual_value || strcmp (actual_value, "") == 0) {
2444 if (xdg.default_value) {
2445 return_value = str_replace ("~", home, xdg.default_value);
2447 return_value = NULL;
2450 return_value = str_replace("~", home, actual_value);
2453 return return_value;
2457 find_xdg_file (int xdg_type, char* filename) {
2458 /* xdg_type = 0 => config
2459 xdg_type = 1 => data
2460 xdg_type = 2 => cache*/
2462 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2463 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2466 gchar* temporary_string;
2470 if (! file_exists (temporary_file) && xdg_type != 2) {
2471 buf = get_xdg_var (XDG[3 + xdg_type]);
2472 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2475 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2476 g_free (temporary_file);
2477 temporary_file = g_strconcat (temporary_string, filename, NULL);
2481 //g_free (temporary_string); - segfaults.
2483 if (file_exists (temporary_file)) {
2484 return temporary_file;
2491 State *s = &uzbl.state;
2492 Network *n = &uzbl.net;
2494 for (i = 0; default_config[i].command != NULL; i++) {
2495 parse_cmd_line(default_config[i].command, NULL);
2498 if (g_strcmp0(s->config_file, "-") == 0) {
2499 s->config_file = NULL;
2503 else if (!s->config_file) {
2504 s->config_file = find_xdg_file (0, "/uzbl/config");
2507 if (s->config_file) {
2508 GArray* lines = read_file_by_line (s->config_file);
2512 while ((line = g_array_index(lines, gchar*, i))) {
2513 parse_cmd_line (line, NULL);
2517 g_array_free (lines, TRUE);
2519 if (uzbl.state.verbose)
2520 printf ("No configuration file loaded.\n");
2523 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2526 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2529 if (!uzbl.behave.cookie_handler)
2532 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2533 GString *s = g_string_new ("");
2534 SoupURI * soup_uri = soup_message_get_uri(msg);
2535 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2536 run_handler(uzbl.behave.cookie_handler, s->str);
2538 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2539 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2540 if ( p != NULL ) *p = '\0';
2541 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2543 if (uzbl.comm.sync_stdout)
2544 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2546 g_string_free(s, TRUE);
2550 save_cookies (SoupMessage *msg, gpointer user_data){
2554 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2555 cookie = soup_cookie_to_set_cookie_header(ck->data);
2556 SoupURI * soup_uri = soup_message_get_uri(msg);
2557 GString *s = g_string_new ("");
2558 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2559 run_handler(uzbl.behave.cookie_handler, s->str);
2561 g_string_free(s, TRUE);
2566 /* --- WEBINSPECTOR --- */
2568 hide_window_cb(GtkWidget *widget, gpointer data) {
2571 gtk_widget_hide(widget);
2574 static WebKitWebView*
2575 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2578 (void) web_inspector;
2579 GtkWidget* scrolled_window;
2580 GtkWidget* new_web_view;
2583 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2584 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2585 G_CALLBACK(hide_window_cb), NULL);
2587 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2588 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2589 gtk_widget_show(g->inspector_window);
2591 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2592 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2593 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2594 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2595 gtk_widget_show(scrolled_window);
2597 new_web_view = webkit_web_view_new();
2598 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2600 return WEBKIT_WEB_VIEW(new_web_view);
2604 inspector_show_window_cb (WebKitWebInspector* inspector){
2606 gtk_widget_show(uzbl.gui.inspector_window);
2610 /* TODO: Add variables and code to make use of these functions */
2612 inspector_close_window_cb (WebKitWebInspector* inspector){
2618 inspector_attach_window_cb (WebKitWebInspector* inspector){
2624 inspector_detach_window_cb (WebKitWebInspector* inspector){
2630 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2636 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2642 set_up_inspector() {
2644 WebKitWebSettings *settings = view_settings();
2645 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2647 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2648 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2649 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2650 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2651 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2652 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2653 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2655 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2659 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2661 uzbl_cmdprop *c = v;
2666 if(c->type == TYPE_STR)
2667 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2668 else if(c->type == TYPE_INT)
2669 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2673 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2677 printf("bind %s = %s %s\n", (char *)k ,
2678 (char *)a->name, a->param?(char *)a->param:"");
2683 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2684 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2689 main (int argc, char* argv[]) {
2690 gtk_init (&argc, &argv);
2691 if (!g_thread_supported ())
2692 g_thread_init (NULL);
2693 uzbl.state.executable_path = g_strdup(argv[0]);
2694 uzbl.state.selected_url = NULL;
2695 uzbl.state.searchtx = NULL;
2697 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2698 g_option_context_add_main_entries (context, entries, NULL);
2699 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2700 g_option_context_parse (context, &argc, &argv, NULL);
2701 g_option_context_free(context);
2703 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2704 if (argc > 1 && !uzbl.state.uri)
2705 uri_override = g_strdup(argv[1]);
2706 gboolean verbose_override = uzbl.state.verbose;
2708 /* initialize hash table */
2709 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2711 uzbl.net.soup_session = webkit_get_default_session();
2712 uzbl.state.keycmd = g_string_new("");
2714 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2715 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2716 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2717 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2718 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2719 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2722 if(uname(&uzbl.state.unameinfo) == -1)
2723 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2725 uzbl.gui.sbar.progress_s = g_strdup("=");
2726 uzbl.gui.sbar.progress_u = g_strdup("·");
2727 uzbl.gui.sbar.progress_w = 10;
2729 /* HTML mode defaults*/
2730 uzbl.behave.html_buffer = g_string_new("");
2731 uzbl.behave.html_endmarker = g_strdup(".");
2732 uzbl.behave.html_timeout = 60;
2733 uzbl.behave.base_url = g_strdup("http://invalid");
2735 /* default mode indicators */
2736 uzbl.behave.insert_indicator = g_strdup("I");
2737 uzbl.behave.cmd_indicator = g_strdup("C");
2741 make_var_to_name_hash();
2743 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2745 uzbl.gui.scrolled_win = create_browser();
2748 /* initial packing */
2749 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2750 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2752 if (uzbl.state.socket_id) {
2753 uzbl.gui.plug = create_plug ();
2754 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2755 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2757 uzbl.gui.main_window = create_window ();
2758 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2759 gtk_widget_show_all (uzbl.gui.main_window);
2760 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2763 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2765 if (uzbl.state.verbose) {
2766 printf("Uzbl start location: %s\n", argv[0]);
2767 if (uzbl.state.socket_id)
2768 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2770 printf("window_id %i\n",(int) uzbl.xwin);
2771 printf("pid %i\n", getpid ());
2772 printf("name: %s\n", uzbl.state.instance_name);
2775 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2776 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2777 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2778 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2779 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2783 if (!uzbl.behave.show_status)
2784 gtk_widget_hide(uzbl.gui.mainbar);
2791 if (verbose_override > uzbl.state.verbose)
2792 uzbl.state.verbose = verbose_override;
2795 set_var_value("uri", uri_override);
2796 g_free(uri_override);
2797 } else if (uzbl.state.uri)
2798 cmd_load_uri(uzbl.gui.web_view, NULL);
2803 return EXIT_SUCCESS;
2806 /* vi: set et ts=4: */