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 window is ok as we the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2122 if (uzbl.gui.main_window)
2123 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2126 if (b->title_format_long) {
2127 parsed = expand_template(b->title_format_long, FALSE);
2128 if (uzbl.gui.main_window)
2129 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2136 key_press_cb (GtkWidget* window, GdkEventKey* event)
2138 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2142 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2143 || 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)
2146 /* turn off insert mode (if always_insert_mode is not used) */
2147 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2148 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2153 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2156 if (event->keyval == GDK_Escape) {
2157 g_string_truncate(uzbl.state.keycmd, 0);
2159 dehilight(uzbl.gui.web_view, NULL, NULL);
2163 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2164 if (event->keyval == GDK_Insert) {
2166 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2167 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2169 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2172 g_string_append (uzbl.state.keycmd, str);
2179 if (event->keyval == GDK_BackSpace)
2180 keycmd_bs(NULL, NULL, NULL);
2182 gboolean key_ret = FALSE;
2183 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2185 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2187 run_keycmd(key_ret);
2189 if (key_ret) return (!uzbl.behave.insert_mode);
2194 run_keycmd(const gboolean key_ret) {
2195 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2197 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2198 g_string_truncate(uzbl.state.keycmd, 0);
2199 parse_command(act->name, act->param, NULL);
2203 /* try if it's an incremental keycmd or one that takes args, and run it */
2204 GString* short_keys = g_string_new ("");
2205 GString* short_keys_inc = g_string_new ("");
2207 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2208 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2209 g_string_assign(short_keys_inc, short_keys->str);
2210 g_string_append_c(short_keys, '_');
2211 g_string_append_c(short_keys_inc, '*');
2213 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2214 /* run normal cmds only if return was pressed */
2215 exec_paramcmd(act, i);
2216 g_string_truncate(uzbl.state.keycmd, 0);
2218 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2219 if (key_ret) /* just quit the incremental command on return */
2220 g_string_truncate(uzbl.state.keycmd, 0);
2221 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2225 g_string_truncate(short_keys, short_keys->len - 1);
2227 g_string_free (short_keys, TRUE);
2228 g_string_free (short_keys_inc, TRUE);
2232 exec_paramcmd(const Action *act, const guint i) {
2233 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2234 GString *actionname = g_string_new ("");
2235 GString *actionparam = g_string_new ("");
2236 g_string_erase (parampart, 0, i+1);
2238 g_string_printf (actionname, act->name, parampart->str);
2240 g_string_printf (actionparam, act->param, parampart->str);
2241 parse_command(actionname->str, actionparam->str, NULL);
2242 g_string_free(actionname, TRUE);
2243 g_string_free(actionparam, TRUE);
2244 g_string_free(parampart, TRUE);
2252 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2253 //main_window_ref = g_object_ref(scrolled_window);
2254 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
2256 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2257 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2259 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2260 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2261 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2268 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2269 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2271 return scrolled_window;
2278 g->mainbar = gtk_hbox_new (FALSE, 0);
2280 /* keep a reference to the bar so we can re-pack it at runtime*/
2281 //sbar_ref = g_object_ref(g->mainbar);
2283 g->mainbar_label = gtk_label_new ("");
2284 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2285 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2286 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2287 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2288 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2289 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2294 GtkWidget* create_window () {
2295 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2296 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2297 gtk_widget_set_name (window, "Uzbl browser");
2298 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2299 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2305 GtkPlug* create_plug () {
2306 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2307 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2308 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2315 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2317 If actname is one that calls an external command, this function will inject
2318 newargs in front of the user-provided args in that command line. They will
2319 come become after the body of the script (in sh) or after the name of
2320 the command to execute (in spawn).
2321 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2322 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2324 The return value consist of two strings: the action (sh, ...) and its args.
2326 If act is not one that calls an external command, then the given action merely
2329 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2330 gchar *actdup = g_strdup(actname);
2331 g_array_append_val(rets, actdup);
2333 if ((g_strcmp0(actname, "spawn") == 0) ||
2334 (g_strcmp0(actname, "sh") == 0) ||
2335 (g_strcmp0(actname, "sync_spawn") == 0) ||
2336 (g_strcmp0(actname, "sync_sh") == 0)) {
2338 GString *a = g_string_new("");
2339 gchar **spawnparts = split_quoted(origargs, FALSE);
2340 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2341 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2343 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2344 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2346 g_array_append_val(rets, a->str);
2347 g_string_free(a, FALSE);
2348 g_strfreev(spawnparts);
2350 gchar *origdup = g_strdup(origargs);
2351 g_array_append_val(rets, origdup);
2353 return (gchar**)g_array_free(rets, FALSE);
2357 run_handler (const gchar *act, const gchar *args) {
2358 /* Consider this code a temporary hack to make the handlers usable.
2359 In practice, all this splicing, injection, and reconstruction is
2360 inefficient, annoying and hard to manage. Potential pitfalls arise
2361 when the handler specific args 1) are not quoted (the handler
2362 callbacks should take care of this) 2) are quoted but interfere
2363 with the users' own quotation. A more ideal solution is
2364 to refactor parse_command so that it doesn't just take a string
2365 and execute it; rather than that, we should have a function which
2366 returns the argument vector parsed from the string. This vector
2367 could be modified (e.g. insert additional args into it) before
2368 passing it to the next function that actually executes it. Though
2369 it still isn't perfect for chain actions.. will reconsider & re-
2370 factor when I have the time. -duc */
2372 char **parts = g_strsplit(act, " ", 2);
2374 if (g_strcmp0(parts[0], "chain") == 0) {
2375 GString *newargs = g_string_new("");
2376 gchar **chainparts = split_quoted(parts[1], FALSE);
2378 /* for every argument in the chain, inject the handler args
2379 and make sure the new parts are wrapped in quotes */
2380 gchar **cp = chainparts;
2382 gchar *quotless = NULL;
2383 gchar **spliced_quotless = NULL; // sigh -_-;
2384 gchar **inpart = NULL;
2387 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2389 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2390 } else quotless = g_strdup(*cp);
2392 spliced_quotless = g_strsplit(quotless, " ", 2);
2393 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2394 g_strfreev(spliced_quotless);
2396 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2402 parse_command(parts[0], &(newargs->str[1]), NULL);
2403 g_string_free(newargs, TRUE);
2404 g_strfreev(chainparts);
2407 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2408 parse_command(inparts[0], inparts[1], NULL);
2416 add_binding (const gchar *key, const gchar *act) {
2417 char **parts = g_strsplit(act, " ", 2);
2424 if (uzbl.state.verbose)
2425 printf ("Binding %-10s : %s\n", key, act);
2426 action = new_action(parts[0], parts[1]);
2428 if (g_hash_table_remove (uzbl.bindings, key))
2429 g_warning ("Overwriting existing binding for \"%s\"", key);
2430 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2435 get_xdg_var (XDG_Var xdg) {
2436 const gchar* actual_value = getenv (xdg.environmental);
2437 const gchar* home = getenv ("HOME");
2438 gchar* return_value;
2440 if (! actual_value || strcmp (actual_value, "") == 0) {
2441 if (xdg.default_value) {
2442 return_value = str_replace ("~", home, xdg.default_value);
2444 return_value = NULL;
2447 return_value = str_replace("~", home, actual_value);
2450 return return_value;
2454 find_xdg_file (int xdg_type, char* filename) {
2455 /* xdg_type = 0 => config
2456 xdg_type = 1 => data
2457 xdg_type = 2 => cache*/
2459 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2460 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2463 gchar* temporary_string;
2467 if (! file_exists (temporary_file) && xdg_type != 2) {
2468 buf = get_xdg_var (XDG[3 + xdg_type]);
2469 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2472 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2473 g_free (temporary_file);
2474 temporary_file = g_strconcat (temporary_string, filename, NULL);
2478 //g_free (temporary_string); - segfaults.
2480 if (file_exists (temporary_file)) {
2481 return temporary_file;
2488 State *s = &uzbl.state;
2489 Network *n = &uzbl.net;
2491 for (i = 0; default_config[i].command != NULL; i++) {
2492 parse_cmd_line(default_config[i].command, NULL);
2495 if (!s->config_file) {
2496 s->config_file = find_xdg_file (0, "/uzbl/config");
2499 if (s->config_file) {
2500 GArray* lines = read_file_by_line (s->config_file);
2504 while ((line = g_array_index(lines, gchar*, i))) {
2505 parse_cmd_line (line, NULL);
2509 g_array_free (lines, TRUE);
2511 if (uzbl.state.verbose)
2512 printf ("No configuration file loaded.\n");
2515 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2518 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2521 if (!uzbl.behave.cookie_handler)
2524 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2525 GString *s = g_string_new ("");
2526 SoupURI * soup_uri = soup_message_get_uri(msg);
2527 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2528 run_handler(uzbl.behave.cookie_handler, s->str);
2530 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2531 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2532 if ( p != NULL ) *p = '\0';
2533 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2535 if (uzbl.comm.sync_stdout)
2536 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2538 g_string_free(s, TRUE);
2542 save_cookies (SoupMessage *msg, gpointer user_data){
2546 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2547 cookie = soup_cookie_to_set_cookie_header(ck->data);
2548 SoupURI * soup_uri = soup_message_get_uri(msg);
2549 GString *s = g_string_new ("");
2550 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2551 run_handler(uzbl.behave.cookie_handler, s->str);
2553 g_string_free(s, TRUE);
2558 /* --- WEBINSPECTOR --- */
2560 hide_window_cb(GtkWidget *widget, gpointer data) {
2563 gtk_widget_hide(widget);
2566 static WebKitWebView*
2567 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2570 (void) web_inspector;
2571 GtkWidget* scrolled_window;
2572 GtkWidget* new_web_view;
2575 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2576 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2577 G_CALLBACK(hide_window_cb), NULL);
2579 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2580 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2581 gtk_widget_show(g->inspector_window);
2583 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2584 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2585 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2586 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2587 gtk_widget_show(scrolled_window);
2589 new_web_view = webkit_web_view_new();
2590 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2592 return WEBKIT_WEB_VIEW(new_web_view);
2596 inspector_show_window_cb (WebKitWebInspector* inspector){
2598 gtk_widget_show(uzbl.gui.inspector_window);
2602 /* TODO: Add variables and code to make use of these functions */
2604 inspector_close_window_cb (WebKitWebInspector* inspector){
2610 inspector_attach_window_cb (WebKitWebInspector* inspector){
2616 inspector_detach_window_cb (WebKitWebInspector* inspector){
2622 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2628 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2634 set_up_inspector() {
2636 WebKitWebSettings *settings = view_settings();
2637 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2639 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2640 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2641 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2642 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2643 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2644 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2645 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2647 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2651 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2653 uzbl_cmdprop *c = v;
2658 if(c->type == TYPE_STR)
2659 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2660 else if(c->type == TYPE_INT)
2661 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2665 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2669 printf("bind %s = %s %s\n", (char *)k ,
2670 (char *)a->name, a->param?(char *)a->param:"");
2675 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2676 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2681 main (int argc, char* argv[]) {
2682 gtk_init (&argc, &argv);
2683 if (!g_thread_supported ())
2684 g_thread_init (NULL);
2685 uzbl.state.executable_path = g_strdup(argv[0]);
2686 uzbl.state.selected_url = NULL;
2687 uzbl.state.searchtx = NULL;
2689 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2690 g_option_context_add_main_entries (context, entries, NULL);
2691 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2692 g_option_context_parse (context, &argc, &argv, NULL);
2693 g_option_context_free(context);
2695 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2696 gboolean verbose_override = uzbl.state.verbose;
2698 /* initialize hash table */
2699 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2701 uzbl.net.soup_session = webkit_get_default_session();
2702 uzbl.state.keycmd = g_string_new("");
2704 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2705 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2706 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2707 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2708 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2709 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2712 if(uname(&uzbl.state.unameinfo) == -1)
2713 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2715 uzbl.gui.sbar.progress_s = g_strdup("=");
2716 uzbl.gui.sbar.progress_u = g_strdup("·");
2717 uzbl.gui.sbar.progress_w = 10;
2719 /* HTML mode defaults*/
2720 uzbl.behave.html_buffer = g_string_new("");
2721 uzbl.behave.html_endmarker = g_strdup(".");
2722 uzbl.behave.html_timeout = 60;
2723 uzbl.behave.base_url = g_strdup("http://invalid");
2725 /* default mode indicators */
2726 uzbl.behave.insert_indicator = g_strdup("I");
2727 uzbl.behave.cmd_indicator = g_strdup("C");
2731 make_var_to_name_hash();
2733 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2735 uzbl.gui.scrolled_win = create_browser();
2738 /* initial packing */
2739 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2740 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2742 if (uzbl.state.socket_id) {
2743 uzbl.gui.plug = create_plug ();
2744 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2745 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2747 uzbl.gui.main_window = create_window ();
2748 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2749 gtk_widget_show_all (uzbl.gui.main_window);
2750 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2753 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2755 if (uzbl.state.verbose) {
2756 printf("Uzbl start location: %s\n", argv[0]);
2757 if (uzbl.state.socket_id)
2758 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2760 printf("window_id %i\n",(int) uzbl.xwin);
2761 printf("pid %i\n", getpid ());
2762 printf("name: %s\n", uzbl.state.instance_name);
2765 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2766 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2767 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2768 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2769 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2773 if (!uzbl.behave.show_status)
2774 gtk_widget_hide(uzbl.gui.mainbar);
2783 if (verbose_override > uzbl.state.verbose)
2784 uzbl.state.verbose = verbose_override;
2787 set_var_value("uri", uri_override);
2788 g_free(uri_override);
2789 } else if (uzbl.state.uri)
2790 cmd_load_uri(uzbl.gui.web_view, NULL);
2795 return EXIT_SUCCESS;
2798 /* vi: set et ts=4: */