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 new_window_load_uri(uri);
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, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
643 if (uzbl.gui.main_title)
644 g_free (uzbl.gui.main_title);
645 uzbl.gui.main_title = g_strdup (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 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
807 set_var_value(g_strstrip(split[0]), value);
813 print(WebKitWebView *page, GArray *argv, GString *result) {
814 (void) page; (void) result;
817 buf = expand(argv_idx(argv, 0), 0);
818 g_string_assign(result, buf);
823 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
824 (void) page; (void) result;
825 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
826 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
827 add_binding(g_strstrip(split[0]), value);
839 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
840 (void) page; (void) result;
842 if (argv_idx(argv, 0)) {
843 if (strcmp (argv_idx(argv, 0), "0") == 0) {
844 uzbl.behave.insert_mode = FALSE;
846 uzbl.behave.insert_mode = TRUE;
849 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
856 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
859 if (argv_idx(argv, 0)) {
860 GString* newuri = g_string_new (argv_idx(argv, 0));
861 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
862 run_js(web_view, argv, NULL);
865 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
866 g_string_prepend (newuri, "http://");
867 /* if we do handle cookies, ask our handler for them */
868 webkit_web_view_load_uri (web_view, newuri->str);
869 g_string_free (newuri, TRUE);
877 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
878 size_t argumentCount, const JSValueRef arguments[],
879 JSValueRef* exception) {
884 JSStringRef js_result_string;
885 GString *result = g_string_new("");
887 if (argumentCount >= 1) {
888 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
889 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
890 char ctl_line[arg_size];
891 JSStringGetUTF8CString(arg, ctl_line, arg_size);
893 parse_cmd_line(ctl_line, result);
895 JSStringRelease(arg);
897 js_result_string = JSStringCreateWithUTF8CString(result->str);
899 g_string_free(result, TRUE);
901 return JSValueMakeString(ctx, js_result_string);
904 static JSStaticFunction js_static_functions[] = {
905 {"run", js_run_command, kJSPropertyAttributeNone},
910 /* This function creates the class and its definition, only once */
911 if (!uzbl.js.initialized) {
912 /* it would be pretty cool to make this dynamic */
913 uzbl.js.classdef = kJSClassDefinitionEmpty;
914 uzbl.js.classdef.staticFunctions = js_static_functions;
916 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
922 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
923 WebKitWebFrame *frame;
924 JSGlobalContextRef context;
925 JSObjectRef globalobject;
926 JSStringRef var_name;
928 JSStringRef js_script;
929 JSValueRef js_result;
930 JSStringRef js_result_string;
931 size_t js_result_size;
935 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
936 context = webkit_web_frame_get_global_context(frame);
937 globalobject = JSContextGetGlobalObject(context);
939 /* uzbl javascript namespace */
940 var_name = JSStringCreateWithUTF8CString("Uzbl");
941 JSObjectSetProperty(context, globalobject, var_name,
942 JSObjectMake(context, uzbl.js.classref, NULL),
943 kJSClassAttributeNone, NULL);
945 /* evaluate the script and get return value*/
946 js_script = JSStringCreateWithUTF8CString(script);
947 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
948 if (js_result && !JSValueIsUndefined(context, js_result)) {
949 js_result_string = JSValueToStringCopy(context, js_result, NULL);
950 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
952 if (js_result_size) {
953 char js_result_utf8[js_result_size];
954 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
955 g_string_assign(result, js_result_utf8);
958 JSStringRelease(js_result_string);
962 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
964 JSStringRelease(var_name);
965 JSStringRelease(js_script);
969 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
971 if (argv_idx(argv, 0))
972 eval_js(web_view, argv_idx(argv, 0), result);
976 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
978 if (argv_idx(argv, 0)) {
979 GArray* lines = read_file_by_line (argv_idx (argv, 0));
984 while ((line = g_array_index(lines, gchar*, i))) {
986 js = g_strdup (line);
988 gchar* newjs = g_strconcat (js, line, NULL);
995 if (uzbl.state.verbose)
996 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
998 if (argv_idx (argv, 1)) {
999 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1003 eval_js (web_view, js, result);
1005 g_array_free (lines, TRUE);
1010 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1011 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1012 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1013 webkit_web_view_unmark_text_matches (page);
1014 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1015 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1019 if (uzbl.state.searchtx) {
1020 if (uzbl.state.verbose)
1021 printf ("Searching: %s\n", uzbl.state.searchtx);
1022 webkit_web_view_set_highlight_text_matches (page, TRUE);
1023 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1028 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1030 search_text(page, argv, TRUE);
1034 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1036 search_text(page, argv, FALSE);
1040 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1041 (void) argv; (void) result;
1042 webkit_web_view_set_highlight_text_matches (page, FALSE);
1047 new_window_load_uri (const gchar * uri) {
1048 GString* to_execute = g_string_new ("");
1049 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1051 for (i = 0; entries[i].long_name != NULL; i++) {
1052 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1053 gchar** str = (gchar**)entries[i].arg_data;
1055 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1059 if (uzbl.state.verbose)
1060 printf("\n%s\n", to_execute->str);
1061 g_spawn_command_line_async (to_execute->str, NULL);
1062 g_string_free (to_execute, TRUE);
1066 chain (WebKitWebView *page, GArray *argv, GString *result) {
1067 (void) page; (void) result;
1069 gchar **parts = NULL;
1071 while ((a = argv_idx(argv, i++))) {
1072 parts = g_strsplit (a, " ", 2);
1073 parse_command(parts[0], parts[1], result);
1079 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1083 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1089 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1093 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1099 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1104 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1106 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1111 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1118 /* --Statusbar functions-- */
1120 build_progressbar_ascii(int percent) {
1121 int width=uzbl.gui.sbar.progress_w;
1124 GString *bar = g_string_new("");
1126 l = (double)percent*((double)width/100.);
1127 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1129 for(i=0; i<(int)l; i++)
1130 g_string_append(bar, uzbl.gui.sbar.progress_s);
1133 g_string_append(bar, uzbl.gui.sbar.progress_u);
1135 return g_string_free(bar, FALSE);
1140 const GScannerConfig scan_config = {
1143 ) /* cset_skip_characters */,
1148 ) /* cset_identifier_first */,
1155 ) /* cset_identifier_nth */,
1156 ( "" ) /* cpair_comment_single */,
1158 TRUE /* case_sensitive */,
1160 FALSE /* skip_comment_multi */,
1161 FALSE /* skip_comment_single */,
1162 FALSE /* scan_comment_multi */,
1163 TRUE /* scan_identifier */,
1164 TRUE /* scan_identifier_1char */,
1165 FALSE /* scan_identifier_NULL */,
1166 TRUE /* scan_symbols */,
1167 FALSE /* scan_binary */,
1168 FALSE /* scan_octal */,
1169 FALSE /* scan_float */,
1170 FALSE /* scan_hex */,
1171 FALSE /* scan_hex_dollar */,
1172 FALSE /* scan_string_sq */,
1173 FALSE /* scan_string_dq */,
1174 TRUE /* numbers_2_int */,
1175 FALSE /* int_2_float */,
1176 FALSE /* identifier_2_string */,
1177 FALSE /* char_2_token */,
1178 FALSE /* symbol_2_token */,
1179 TRUE /* scope_0_fallback */,
1184 uzbl.scan = g_scanner_new(&scan_config);
1185 while(symp->symbol_name) {
1186 g_scanner_scope_add_symbol(uzbl.scan, 0,
1188 GINT_TO_POINTER(symp->symbol_token));
1194 expand_template(const char *template, gboolean escape_markup) {
1195 if(!template) return NULL;
1197 GTokenType token = G_TOKEN_NONE;
1198 GString *ret = g_string_new("");
1202 g_scanner_input_text(uzbl.scan, template, strlen(template));
1203 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1204 token = g_scanner_get_next_token(uzbl.scan);
1206 if(token == G_TOKEN_SYMBOL) {
1207 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1211 buf = uzbl.state.uri?
1212 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1213 g_string_append(ret, buf);
1217 g_string_append(ret, uzbl.state.uri?
1218 uzbl.state.uri:g_strdup(""));
1221 buf = itos(uzbl.gui.sbar.load_progress);
1222 g_string_append(ret, buf);
1225 case SYM_LOADPRGSBAR:
1226 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1227 g_string_append(ret, buf);
1232 buf = uzbl.gui.main_title?
1233 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1234 g_string_append(ret, buf);
1238 g_string_append(ret, uzbl.gui.main_title?
1239 uzbl.gui.main_title:g_strdup(""));
1241 case SYM_SELECTED_URI:
1243 buf = uzbl.state.selected_url?
1244 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1245 g_string_append(ret, buf);
1249 g_string_append(ret, uzbl.state.selected_url?
1250 uzbl.state.selected_url:g_strdup(""));
1253 buf = itos(uzbl.xwin);
1254 g_string_append(ret,
1255 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1260 buf = uzbl.state.keycmd->str?
1261 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1262 g_string_append(ret, buf);
1266 g_string_append(ret, uzbl.state.keycmd->str?
1267 uzbl.state.keycmd->str:g_strdup(""));
1270 g_string_append(ret,
1271 uzbl.behave.insert_mode?
1272 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1275 g_string_append(ret,
1276 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1278 /* useragent syms */
1280 buf = itos(WEBKIT_MAJOR_VERSION);
1281 g_string_append(ret, buf);
1285 buf = itos(WEBKIT_MINOR_VERSION);
1286 g_string_append(ret, buf);
1290 buf = itos(WEBKIT_MICRO_VERSION);
1291 g_string_append(ret, buf);
1295 g_string_append(ret, uzbl.state.unameinfo.sysname);
1298 g_string_append(ret, uzbl.state.unameinfo.nodename);
1301 g_string_append(ret, uzbl.state.unameinfo.release);
1304 g_string_append(ret, uzbl.state.unameinfo.version);
1307 g_string_append(ret, uzbl.state.unameinfo.machine);
1310 g_string_append(ret, ARCH);
1313 case SYM_DOMAINNAME:
1314 g_string_append(ret, uzbl.state.unameinfo.domainname);
1318 g_string_append(ret, COMMIT);
1324 else if(token == G_TOKEN_INT) {
1325 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1326 g_string_append(ret, buf);
1329 else if(token == G_TOKEN_IDENTIFIER) {
1330 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1332 else if(token == G_TOKEN_CHAR) {
1333 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1337 return g_string_free(ret, FALSE);
1339 /* --End Statusbar functions-- */
1342 sharg_append(GArray *a, const gchar *str) {
1343 const gchar *s = (str ? str : "");
1344 g_array_append_val(a, s);
1347 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1349 run_command (const gchar *command, const guint npre, const gchar **args,
1350 const gboolean sync, char **output_stdout) {
1351 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1354 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1355 gchar *pid = itos(getpid());
1356 gchar *xwin = itos(uzbl.xwin);
1358 sharg_append(a, command);
1359 for (i = 0; i < npre; i++) /* add n args before the default vars */
1360 sharg_append(a, args[i]);
1361 sharg_append(a, uzbl.state.config_file);
1362 sharg_append(a, pid);
1363 sharg_append(a, xwin);
1364 sharg_append(a, uzbl.comm.fifo_path);
1365 sharg_append(a, uzbl.comm.socket_path);
1366 sharg_append(a, uzbl.state.uri);
1367 sharg_append(a, uzbl.gui.main_title);
1369 for (i = npre; i < g_strv_length((gchar**)args); i++)
1370 sharg_append(a, args[i]);
1374 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1376 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1377 NULL, NULL, output_stdout, NULL, NULL, &err);
1378 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1379 NULL, NULL, NULL, &err);
1381 if (uzbl.state.verbose) {
1382 GString *s = g_string_new("spawned:");
1383 for (i = 0; i < (a->len); i++) {
1384 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1385 g_string_append_printf(s, " %s", qarg);
1388 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1389 printf("%s\n", s->str);
1390 g_string_free(s, TRUE);
1392 printf("Stdout: %s\n", *output_stdout);
1396 g_printerr("error on run_command: %s\n", err->message);
1401 g_array_free (a, TRUE);
1406 split_quoted(const gchar* src, const gboolean unquote) {
1407 /* split on unquoted space, return array of strings;
1408 remove a layer of quotes and backslashes if unquote */
1409 if (!src) return NULL;
1411 gboolean dq = FALSE;
1412 gboolean sq = FALSE;
1413 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1414 GString *s = g_string_new ("");
1418 for (p = src; *p != '\0'; p++) {
1419 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1420 else if (*p == '\\') { g_string_append_c(s, *p++);
1421 g_string_append_c(s, *p); }
1422 else if ((*p == '"') && unquote && !sq) dq = !dq;
1423 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1425 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1426 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1428 else if ((*p == ' ') && !dq && !sq) {
1429 dup = g_strdup(s->str);
1430 g_array_append_val(a, dup);
1431 g_string_truncate(s, 0);
1432 } else g_string_append_c(s, *p);
1434 dup = g_strdup(s->str);
1435 g_array_append_val(a, dup);
1436 ret = (gchar**)a->data;
1437 g_array_free (a, FALSE);
1438 g_string_free (s, TRUE);
1443 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1444 (void)web_view; (void)result;
1445 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1446 if (argv_idx(argv, 0))
1447 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1451 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1452 (void)web_view; (void)result;
1454 if (argv_idx(argv, 0))
1455 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1456 TRUE, &uzbl.comm.sync_stdout);
1460 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1461 (void)web_view; (void)result;
1462 if (!uzbl.behave.shell_cmd) {
1463 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1468 gchar *spacer = g_strdup("");
1469 g_array_insert_val(argv, 1, spacer);
1470 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1472 for (i = 1; i < g_strv_length(cmd); i++)
1473 g_array_prepend_val(argv, cmd[i]);
1475 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1481 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1482 (void)web_view; (void)result;
1483 if (!uzbl.behave.shell_cmd) {
1484 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1489 gchar *spacer = g_strdup("");
1490 g_array_insert_val(argv, 1, spacer);
1491 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1493 for (i = 1; i < g_strv_length(cmd); i++)
1494 g_array_prepend_val(argv, cmd[i]);
1496 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1497 TRUE, &uzbl.comm.sync_stdout);
1503 parse_command(const char *cmd, const char *param, GString *result) {
1506 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1508 gchar **par = split_quoted(param, TRUE);
1509 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1511 if (c->no_split) { /* don't split */
1512 sharg_append(a, param);
1514 for (i = 0; i < g_strv_length(par); i++)
1515 sharg_append(a, par[i]);
1518 if (result == NULL) {
1519 GString *result_print = g_string_new("");
1521 c->function(uzbl.gui.web_view, a, result_print);
1522 if (result_print->len)
1523 printf("%*s\n", result_print->len, result_print->str);
1525 g_string_free(result_print, TRUE);
1527 c->function(uzbl.gui.web_view, a, result);
1530 g_array_free (a, TRUE);
1533 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1540 if(*uzbl.net.proxy_url == ' '
1541 || uzbl.net.proxy_url == NULL) {
1542 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1543 (GType) SOUP_SESSION_PROXY_URI);
1546 suri = soup_uri_new(uzbl.net.proxy_url);
1547 g_object_set(G_OBJECT(uzbl.net.soup_session),
1548 SOUP_SESSION_PROXY_URI,
1550 soup_uri_free(suri);
1557 if(file_exists(uzbl.gui.icon)) {
1558 if (uzbl.gui.main_window)
1559 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1561 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1563 g_free (uzbl.gui.icon);
1568 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1569 g_array_append_val (a, uzbl.state.uri);
1570 load_uri(uzbl.gui.web_view, a, NULL);
1571 g_array_free (a, TRUE);
1575 cmd_always_insert_mode() {
1576 uzbl.behave.insert_mode =
1577 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1583 g_object_set(G_OBJECT(uzbl.net.soup_session),
1584 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1588 cmd_max_conns_host() {
1589 g_object_set(G_OBJECT(uzbl.net.soup_session),
1590 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1595 soup_session_remove_feature
1596 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1597 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1598 /*g_free(uzbl.net.soup_logger);*/
1600 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1601 soup_session_add_feature(uzbl.net.soup_session,
1602 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1605 static WebKitWebSettings*
1607 return webkit_web_view_get_settings(uzbl.gui.web_view);
1612 WebKitWebSettings *ws = view_settings();
1613 if (uzbl.behave.font_size > 0) {
1614 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1617 if (uzbl.behave.monospace_size > 0) {
1618 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1619 uzbl.behave.monospace_size, NULL);
1621 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1622 uzbl.behave.font_size, NULL);
1628 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1632 cmd_disable_plugins() {
1633 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1634 !uzbl.behave.disable_plugins, NULL);
1638 cmd_disable_scripts() {
1639 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1640 !uzbl.behave.disable_scripts, NULL);
1644 cmd_minimum_font_size() {
1645 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1646 uzbl.behave.minimum_font_size, NULL);
1649 cmd_autoload_img() {
1650 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1651 uzbl.behave.autoload_img, NULL);
1656 cmd_autoshrink_img() {
1657 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1658 uzbl.behave.autoshrink_img, NULL);
1663 cmd_enable_spellcheck() {
1664 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1665 uzbl.behave.enable_spellcheck, NULL);
1669 cmd_enable_private() {
1670 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1671 uzbl.behave.enable_private, NULL);
1676 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1677 uzbl.behave.print_bg, NULL);
1682 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1683 uzbl.behave.style_uri, NULL);
1687 cmd_resizable_txt() {
1688 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1689 uzbl.behave.resizable_txt, NULL);
1693 cmd_default_encoding() {
1694 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1695 uzbl.behave.default_encoding, NULL);
1699 cmd_enforce_96dpi() {
1700 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1701 uzbl.behave.enforce_96dpi, NULL);
1705 cmd_caret_browsing() {
1706 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1707 uzbl.behave.caret_browsing, NULL);
1711 cmd_cookie_handler() {
1712 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1713 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1714 if ((g_strcmp0(split[0], "sh") == 0) ||
1715 (g_strcmp0(split[0], "spawn") == 0)) {
1716 g_free (uzbl.behave.cookie_handler);
1717 uzbl.behave.cookie_handler =
1718 g_strdup_printf("sync_%s %s", split[0], split[1]);
1725 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1730 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1735 if(uzbl.behave.inject_html) {
1736 webkit_web_view_load_html_string (uzbl.gui.web_view,
1737 uzbl.behave.inject_html, NULL);
1746 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1747 uzbl.behave.modmask = 0;
1749 if(uzbl.behave.modkey)
1750 g_free(uzbl.behave.modkey);
1751 uzbl.behave.modkey = buf;
1753 for (i = 0; modkeys[i].key != NULL; i++) {
1754 if (g_strrstr(buf, modkeys[i].key))
1755 uzbl.behave.modmask |= modkeys[i].mask;
1761 if (*uzbl.net.useragent == ' ') {
1762 g_free (uzbl.net.useragent);
1763 uzbl.net.useragent = NULL;
1765 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1767 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1768 g_free(uzbl.net.useragent);
1769 uzbl.net.useragent = ua;
1775 gtk_widget_ref(uzbl.gui.scrolled_win);
1776 gtk_widget_ref(uzbl.gui.mainbar);
1777 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1778 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1780 if(uzbl.behave.status_top) {
1781 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1782 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1785 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.mainbar, FALSE, TRUE, 0);
1788 gtk_widget_unref(uzbl.gui.scrolled_win);
1789 gtk_widget_unref(uzbl.gui.mainbar);
1790 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1795 set_var_value(gchar *name, gchar *val) {
1796 uzbl_cmdprop *c = NULL;
1800 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1801 /* check for the variable type */
1802 if (c->type == TYPE_STR) {
1803 buf = expand(val, 0);
1806 } else if(c->type == TYPE_INT) {
1807 int *ip = (int *)c->ptr;
1808 buf = expand(val, 0);
1809 *ip = (int)strtoul(buf, &endp, 10);
1811 } else if (c->type == TYPE_FLOAT) {
1812 float *fp = (float *)c->ptr;
1813 buf = expand(val, 0);
1814 *fp = strtod(buf, &endp);
1818 /* invoke a command specific function */
1819 if(c->func) c->func();
1826 Behaviour *b = &uzbl.behave;
1828 if(b->html_buffer->str) {
1829 webkit_web_view_load_html_string (uzbl.gui.web_view,
1830 b->html_buffer->str, b->base_url);
1831 g_string_free(b->html_buffer, TRUE);
1832 b->html_buffer = g_string_new("");
1836 enum {M_CMD, M_HTML};
1838 parse_cmd_line(const char *ctl_line, GString *result) {
1839 Behaviour *b = &uzbl.behave;
1842 if(b->mode == M_HTML) {
1843 len = strlen(b->html_endmarker);
1844 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1845 if(len == strlen(ctl_line)-1 &&
1846 !strncmp(b->html_endmarker, ctl_line, len)) {
1848 set_var_value("mode", "0");
1853 set_timeout(b->html_timeout);
1854 g_string_append(b->html_buffer, ctl_line);
1857 else if((ctl_line[0] == '#') /* Comments */
1858 || (ctl_line[0] == ' ')
1859 || (ctl_line[0] == '\n'))
1860 ; /* ignore these lines */
1861 else { /* parse a command */
1863 gchar **tokens = NULL;
1864 len = strlen(ctl_line);
1866 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1867 ctlstrip = g_strndup(ctl_line, len - 1);
1868 else ctlstrip = g_strdup(ctl_line);
1870 tokens = g_strsplit(ctlstrip, " ", 2);
1871 parse_command(tokens[0], tokens[1], result);
1878 build_stream_name(int type, const gchar* dir) {
1879 char *xwin_str = NULL;
1880 State *s = &uzbl.state;
1883 xwin_str = itos((int)uzbl.xwin);
1885 str = g_strdup_printf
1886 ("%s/uzbl_fifo_%s", dir,
1887 s->instance_name ? s->instance_name : xwin_str);
1888 } else if (type == SOCKET) {
1889 str = g_strdup_printf
1890 ("%s/uzbl_socket_%s", dir,
1891 s->instance_name ? s->instance_name : xwin_str );
1898 control_fifo(GIOChannel *gio, GIOCondition condition) {
1899 if (uzbl.state.verbose)
1900 printf("triggered\n");
1905 if (condition & G_IO_HUP)
1906 g_error ("Fifo: Read end of pipe died!\n");
1909 g_error ("Fifo: GIOChannel broke\n");
1911 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1912 if (ret == G_IO_STATUS_ERROR) {
1913 g_error ("Fifo: Error reading: %s\n", err->message);
1917 parse_cmd_line(ctl_line, NULL);
1924 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1925 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1926 if (unlink(uzbl.comm.fifo_path) == -1)
1927 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1928 g_free(uzbl.comm.fifo_path);
1929 uzbl.comm.fifo_path = NULL;
1932 if (*dir == ' ') { /* space unsets the variable */
1937 GIOChannel *chan = NULL;
1938 GError *error = NULL;
1939 gchar *path = build_stream_name(FIFO, dir);
1941 if (!file_exists(path)) {
1942 if (mkfifo (path, 0666) == 0) {
1943 // 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.
1944 chan = g_io_channel_new_file(path, "r+", &error);
1946 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1947 if (uzbl.state.verbose)
1948 printf ("init_fifo: created successfully as %s\n", path);
1949 uzbl.comm.fifo_path = path;
1951 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1952 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1953 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1954 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1956 /* if we got this far, there was an error; cleanup */
1957 if (error) g_error_free (error);
1964 control_stdin(GIOChannel *gio, GIOCondition condition) {
1966 gchar *ctl_line = NULL;
1969 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1970 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1973 parse_cmd_line(ctl_line, NULL);
1981 GIOChannel *chan = NULL;
1982 GError *error = NULL;
1984 chan = g_io_channel_unix_new(fileno(stdin));
1986 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1987 g_error ("Stdin: could not add watch\n");
1989 if (uzbl.state.verbose)
1990 printf ("Stdin: watch added successfully\n");
1993 g_error ("Stdin: Error while opening: %s\n", error->message);
1995 if (error) g_error_free (error);
1999 control_socket(GIOChannel *chan) {
2000 struct sockaddr_un remote;
2001 unsigned int t = sizeof(remote);
2003 GIOChannel *clientchan;
2005 clientsock = accept (g_io_channel_unix_get_fd(chan),
2006 (struct sockaddr *) &remote, &t);
2008 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2009 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2010 (GIOFunc) control_client_socket, clientchan);
2017 control_client_socket(GIOChannel *clientchan) {
2019 GString *result = g_string_new("");
2020 GError *error = NULL;
2024 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2025 if (ret == G_IO_STATUS_ERROR) {
2026 g_warning ("Error reading: %s\n", error->message);
2027 g_io_channel_shutdown(clientchan, TRUE, &error);
2029 } else if (ret == G_IO_STATUS_EOF) {
2030 /* shutdown and remove channel watch from main loop */
2031 g_io_channel_shutdown(clientchan, TRUE, &error);
2036 parse_cmd_line (ctl_line, result);
2037 g_string_append_c(result, '\n');
2038 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2040 if (ret == G_IO_STATUS_ERROR) {
2041 g_warning ("Error writing: %s", error->message);
2043 g_io_channel_flush(clientchan, &error);
2046 if (error) g_error_free (error);
2047 g_string_free(result, TRUE);
2053 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2054 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2055 if (unlink(uzbl.comm.socket_path) == -1)
2056 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2057 g_free(uzbl.comm.socket_path);
2058 uzbl.comm.socket_path = NULL;
2066 GIOChannel *chan = NULL;
2068 struct sockaddr_un local;
2069 gchar *path = build_stream_name(SOCKET, dir);
2071 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2073 local.sun_family = AF_UNIX;
2074 strcpy (local.sun_path, path);
2075 unlink (local.sun_path);
2077 len = strlen (local.sun_path) + sizeof (local.sun_family);
2078 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2079 if (uzbl.state.verbose)
2080 printf ("init_socket: opened in %s\n", path);
2083 if( (chan = g_io_channel_unix_new(sock)) ) {
2084 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2085 uzbl.comm.socket_path = path;
2088 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2090 /* if we got this far, there was an error; cleanup */
2097 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2098 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2100 // this function may be called very early when the templates are not set (yet), hence the checks
2102 update_title (void) {
2103 Behaviour *b = &uzbl.behave;
2106 if (b->show_status) {
2107 if (b->title_format_short) {
2108 parsed = expand_template(b->title_format_short, FALSE);
2109 if (uzbl.gui.main_window)
2110 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2113 if (b->status_format) {
2114 parsed = expand_template(b->status_format, TRUE);
2115 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2118 if (b->status_background) {
2120 gdk_color_parse (b->status_background, &color);
2121 //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)
2122 gtk_widget_modify_bg (uzbl.gui.vbox, GTK_STATE_NORMAL, &color);
2125 if (b->title_format_long) {
2126 parsed = expand_template(b->title_format_long, FALSE);
2127 if (uzbl.gui.main_window)
2128 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2135 key_press_cb (GtkWidget* window, GdkEventKey* event)
2137 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2141 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2142 || 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)
2145 /* turn off insert mode (if always_insert_mode is not used) */
2146 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2147 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2152 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2155 if (event->keyval == GDK_Escape) {
2156 g_string_truncate(uzbl.state.keycmd, 0);
2158 dehilight(uzbl.gui.web_view, NULL, NULL);
2162 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2163 if (event->keyval == GDK_Insert) {
2165 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2166 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2168 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2171 g_string_append (uzbl.state.keycmd, str);
2178 if (event->keyval == GDK_BackSpace)
2179 keycmd_bs(NULL, NULL, NULL);
2181 gboolean key_ret = FALSE;
2182 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2184 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2186 run_keycmd(key_ret);
2188 if (key_ret) return (!uzbl.behave.insert_mode);
2193 run_keycmd(const gboolean key_ret) {
2194 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2196 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2197 g_string_truncate(uzbl.state.keycmd, 0);
2198 parse_command(act->name, act->param, NULL);
2202 /* try if it's an incremental keycmd or one that takes args, and run it */
2203 GString* short_keys = g_string_new ("");
2204 GString* short_keys_inc = g_string_new ("");
2206 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2207 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2208 g_string_assign(short_keys_inc, short_keys->str);
2209 g_string_append_c(short_keys, '_');
2210 g_string_append_c(short_keys_inc, '*');
2212 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2213 /* run normal cmds only if return was pressed */
2214 exec_paramcmd(act, i);
2215 g_string_truncate(uzbl.state.keycmd, 0);
2217 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2218 if (key_ret) /* just quit the incremental command on return */
2219 g_string_truncate(uzbl.state.keycmd, 0);
2220 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2224 g_string_truncate(short_keys, short_keys->len - 1);
2226 g_string_free (short_keys, TRUE);
2227 g_string_free (short_keys_inc, TRUE);
2231 exec_paramcmd(const Action *act, const guint i) {
2232 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2233 GString *actionname = g_string_new ("");
2234 GString *actionparam = g_string_new ("");
2235 g_string_erase (parampart, 0, i+1);
2237 g_string_printf (actionname, act->name, parampart->str);
2239 g_string_printf (actionparam, act->param, parampart->str);
2240 parse_command(actionname->str, actionparam->str, NULL);
2241 g_string_free(actionname, TRUE);
2242 g_string_free(actionparam, TRUE);
2243 g_string_free(parampart, TRUE);
2251 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2252 //main_window_ref = g_object_ref(scrolled_window);
2253 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
2255 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2256 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2258 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2259 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2268 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2270 return scrolled_window;
2277 g->mainbar = gtk_hbox_new (FALSE, 0);
2279 /* keep a reference to the bar so we can re-pack it at runtime*/
2280 //sbar_ref = g_object_ref(g->mainbar);
2282 g->mainbar_label = gtk_label_new ("");
2283 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2284 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2285 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2286 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2287 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2288 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2293 GtkWidget* create_window () {
2294 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2295 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2296 gtk_widget_set_name (window, "Uzbl browser");
2297 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2298 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2304 GtkPlug* create_plug () {
2305 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2306 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2307 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2314 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2316 If actname is one that calls an external command, this function will inject
2317 newargs in front of the user-provided args in that command line. They will
2318 come become after the body of the script (in sh) or after the name of
2319 the command to execute (in spawn).
2320 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2321 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2323 The return value consist of two strings: the action (sh, ...) and its args.
2325 If act is not one that calls an external command, then the given action merely
2328 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2329 gchar *actdup = g_strdup(actname);
2330 g_array_append_val(rets, actdup);
2332 if ((g_strcmp0(actname, "spawn") == 0) ||
2333 (g_strcmp0(actname, "sh") == 0) ||
2334 (g_strcmp0(actname, "sync_spawn") == 0) ||
2335 (g_strcmp0(actname, "sync_sh") == 0)) {
2337 GString *a = g_string_new("");
2338 gchar **spawnparts = split_quoted(origargs, FALSE);
2339 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2340 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2342 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2343 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2345 g_array_append_val(rets, a->str);
2346 g_string_free(a, FALSE);
2347 g_strfreev(spawnparts);
2349 gchar *origdup = g_strdup(origargs);
2350 g_array_append_val(rets, origdup);
2352 return (gchar**)g_array_free(rets, FALSE);
2356 run_handler (const gchar *act, const gchar *args) {
2357 /* Consider this code a temporary hack to make the handlers usable.
2358 In practice, all this splicing, injection, and reconstruction is
2359 inefficient, annoying and hard to manage. Potential pitfalls arise
2360 when the handler specific args 1) are not quoted (the handler
2361 callbacks should take care of this) 2) are quoted but interfere
2362 with the users' own quotation. A more ideal solution is
2363 to refactor parse_command so that it doesn't just take a string
2364 and execute it; rather than that, we should have a function which
2365 returns the argument vector parsed from the string. This vector
2366 could be modified (e.g. insert additional args into it) before
2367 passing it to the next function that actually executes it. Though
2368 it still isn't perfect for chain actions.. will reconsider & re-
2369 factor when I have the time. -duc */
2371 char **parts = g_strsplit(act, " ", 2);
2373 if (g_strcmp0(parts[0], "chain") == 0) {
2374 GString *newargs = g_string_new("");
2375 gchar **chainparts = split_quoted(parts[1], FALSE);
2377 /* for every argument in the chain, inject the handler args
2378 and make sure the new parts are wrapped in quotes */
2379 gchar **cp = chainparts;
2381 gchar *quotless = NULL;
2382 gchar **spliced_quotless = NULL; // sigh -_-;
2383 gchar **inpart = NULL;
2386 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2388 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2389 } else quotless = g_strdup(*cp);
2391 spliced_quotless = g_strsplit(quotless, " ", 2);
2392 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2393 g_strfreev(spliced_quotless);
2395 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2401 parse_command(parts[0], &(newargs->str[1]), NULL);
2402 g_string_free(newargs, TRUE);
2403 g_strfreev(chainparts);
2406 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2407 parse_command(inparts[0], inparts[1], NULL);
2415 add_binding (const gchar *key, const gchar *act) {
2416 char **parts = g_strsplit(act, " ", 2);
2423 if (uzbl.state.verbose)
2424 printf ("Binding %-10s : %s\n", key, act);
2425 action = new_action(parts[0], parts[1]);
2427 if (g_hash_table_remove (uzbl.bindings, key))
2428 g_warning ("Overwriting existing binding for \"%s\"", key);
2429 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2434 get_xdg_var (XDG_Var xdg) {
2435 const gchar* actual_value = getenv (xdg.environmental);
2436 const gchar* home = getenv ("HOME");
2437 gchar* return_value;
2439 if (! actual_value || strcmp (actual_value, "") == 0) {
2440 if (xdg.default_value) {
2441 return_value = str_replace ("~", home, xdg.default_value);
2443 return_value = NULL;
2446 return_value = str_replace("~", home, actual_value);
2449 return return_value;
2453 find_xdg_file (int xdg_type, char* filename) {
2454 /* xdg_type = 0 => config
2455 xdg_type = 1 => data
2456 xdg_type = 2 => cache*/
2458 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2459 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2462 gchar* temporary_string;
2466 if (! file_exists (temporary_file) && xdg_type != 2) {
2467 buf = get_xdg_var (XDG[3 + xdg_type]);
2468 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2471 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2472 g_free (temporary_file);
2473 temporary_file = g_strconcat (temporary_string, filename, NULL);
2477 //g_free (temporary_string); - segfaults.
2479 if (file_exists (temporary_file)) {
2480 return temporary_file;
2487 State *s = &uzbl.state;
2488 Network *n = &uzbl.net;
2490 for (i = 0; default_config[i].command != NULL; i++) {
2491 parse_cmd_line(default_config[i].command, NULL);
2494 if (!s->config_file) {
2495 s->config_file = find_xdg_file (0, "/uzbl/config");
2498 if (s->config_file) {
2499 GArray* lines = read_file_by_line (s->config_file);
2503 while ((line = g_array_index(lines, gchar*, i))) {
2504 parse_cmd_line (line, NULL);
2508 g_array_free (lines, TRUE);
2510 if (uzbl.state.verbose)
2511 printf ("No configuration file loaded.\n");
2514 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2517 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2520 if (!uzbl.behave.cookie_handler)
2523 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2524 GString *s = g_string_new ("");
2525 SoupURI * soup_uri = soup_message_get_uri(msg);
2526 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2527 run_handler(uzbl.behave.cookie_handler, s->str);
2529 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2530 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2531 if ( p != NULL ) *p = '\0';
2532 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2534 if (uzbl.comm.sync_stdout)
2535 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2537 g_string_free(s, TRUE);
2541 save_cookies (SoupMessage *msg, gpointer user_data){
2545 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2546 cookie = soup_cookie_to_set_cookie_header(ck->data);
2547 SoupURI * soup_uri = soup_message_get_uri(msg);
2548 GString *s = g_string_new ("");
2549 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2550 run_handler(uzbl.behave.cookie_handler, s->str);
2552 g_string_free(s, TRUE);
2557 /* --- WEBINSPECTOR --- */
2559 hide_window_cb(GtkWidget *widget, gpointer data) {
2562 gtk_widget_hide(widget);
2565 static WebKitWebView*
2566 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2569 (void) web_inspector;
2570 GtkWidget* scrolled_window;
2571 GtkWidget* new_web_view;
2574 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2575 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2576 G_CALLBACK(hide_window_cb), NULL);
2578 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2579 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2580 gtk_widget_show(g->inspector_window);
2582 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2583 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2584 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2585 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2586 gtk_widget_show(scrolled_window);
2588 new_web_view = webkit_web_view_new();
2589 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2591 return WEBKIT_WEB_VIEW(new_web_view);
2595 inspector_show_window_cb (WebKitWebInspector* inspector){
2597 gtk_widget_show(uzbl.gui.inspector_window);
2601 /* TODO: Add variables and code to make use of these functions */
2603 inspector_close_window_cb (WebKitWebInspector* inspector){
2609 inspector_attach_window_cb (WebKitWebInspector* inspector){
2615 inspector_detach_window_cb (WebKitWebInspector* inspector){
2621 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2627 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2633 set_up_inspector() {
2635 WebKitWebSettings *settings = view_settings();
2636 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2638 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2639 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2640 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2641 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2642 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2643 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2644 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2646 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2650 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2652 uzbl_cmdprop *c = v;
2657 if(c->type == TYPE_STR)
2658 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2659 else if(c->type == TYPE_INT)
2660 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2664 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2668 printf("bind %s = %s %s\n", (char *)k ,
2669 (char *)a->name, a->param?(char *)a->param:"");
2674 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2675 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2680 main (int argc, char* argv[]) {
2681 gtk_init (&argc, &argv);
2682 if (!g_thread_supported ())
2683 g_thread_init (NULL);
2684 uzbl.state.executable_path = g_strdup(argv[0]);
2685 uzbl.state.selected_url = NULL;
2686 uzbl.state.searchtx = NULL;
2688 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2689 g_option_context_add_main_entries (context, entries, NULL);
2690 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2691 g_option_context_parse (context, &argc, &argv, NULL);
2692 g_option_context_free(context);
2694 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2695 gboolean verbose_override = uzbl.state.verbose;
2697 /* initialize hash table */
2698 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2700 uzbl.net.soup_session = webkit_get_default_session();
2701 uzbl.state.keycmd = g_string_new("");
2703 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2704 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2705 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2706 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2707 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2708 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2711 if(uname(&uzbl.state.unameinfo) == -1)
2712 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2714 uzbl.gui.sbar.progress_s = g_strdup("=");
2715 uzbl.gui.sbar.progress_u = g_strdup("·");
2716 uzbl.gui.sbar.progress_w = 10;
2718 /* HTML mode defaults*/
2719 uzbl.behave.html_buffer = g_string_new("");
2720 uzbl.behave.html_endmarker = g_strdup(".");
2721 uzbl.behave.html_timeout = 60;
2722 uzbl.behave.base_url = g_strdup("http://invalid");
2724 /* default mode indicators */
2725 uzbl.behave.insert_indicator = g_strdup("I");
2726 uzbl.behave.cmd_indicator = g_strdup("C");
2730 make_var_to_name_hash();
2732 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2734 uzbl.gui.scrolled_win = create_browser();
2737 /* initial packing */
2738 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2739 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2741 if (uzbl.state.socket_id) {
2742 uzbl.gui.plug = create_plug ();
2743 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2744 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2746 uzbl.gui.main_window = create_window ();
2747 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2748 gtk_widget_show_all (uzbl.gui.main_window);
2749 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2752 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2754 if (uzbl.state.verbose) {
2755 printf("Uzbl start location: %s\n", argv[0]);
2756 if (uzbl.state.socket_id)
2757 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2759 printf("window_id %i\n",(int) uzbl.xwin);
2760 printf("pid %i\n", getpid ());
2761 printf("name: %s\n", uzbl.state.instance_name);
2764 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2765 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2766 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2767 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2768 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2772 if (!uzbl.behave.show_status)
2773 gtk_widget_hide(uzbl.gui.mainbar);
2782 if (verbose_override > uzbl.state.verbose)
2783 uzbl.state.verbose = verbose_override;
2786 set_var_value("uri", uri_override);
2787 g_free(uri_override);
2788 } else if (uzbl.state.uri)
2789 cmd_load_uri(uzbl.gui.web_view, NULL);
2794 return EXIT_SUCCESS;
2797 /* vi: set et ts=4: */