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
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
65 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
67 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69 { NULL, 0, 0, 0, NULL, NULL, NULL }
73 /* associate command names to their properties */
74 typedef const struct {
80 enum {TYPE_INT, TYPE_STR};
82 /* an abbreviation to help keep the table's width humane */
83 #define PTR(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .func = fun }
88 } var_name_to_ptr[] = {
89 /* variable name pointer to variable in code type callback function */
90 /* --------------------------------------------------------------------------------------- */
91 { "uri", PTR(uzbl.state.uri, STR, cmd_load_uri)},
92 { "status_message", PTR(uzbl.gui.sbar.msg, STR, update_title)},
93 { "show_status", PTR(uzbl.behave.show_status, INT, cmd_set_status)},
94 { "status_top", PTR(uzbl.behave.status_top, INT, move_statusbar)},
95 { "status_format", PTR(uzbl.behave.status_format, STR, update_title)},
96 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, update_title)},
97 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, update_title)},
98 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, update_title)},
99 { "status_background", PTR(uzbl.behave.status_background, STR, update_title)},
100 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, update_title)},
101 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, update_title)},
102 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, NULL)},
103 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, cmd_always_insert_mode)},
104 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, NULL)},
105 { "modkey", PTR(uzbl.behave.modkey, STR, cmd_modkey)},
106 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, NULL)},
107 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, NULL)},
108 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, NULL)},
109 { "history_handler", PTR(uzbl.behave.history_handler, STR, NULL)},
110 { "download_handler", PTR(uzbl.behave.download_handler, STR, NULL)},
111 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, NULL)},
112 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, cmd_fifo_dir)},
113 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, cmd_socket_dir)},
114 { "http_debug", PTR(uzbl.behave.http_debug, INT, cmd_http_debug)},
115 { "font_size", PTR(uzbl.behave.font_size, INT, cmd_font_size)},
116 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, cmd_font_size)},
117 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, cmd_minimum_font_size)},
118 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, cmd_disable_plugins)},
119 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, NULL)},
120 { "proxy_url", PTR(uzbl.net.proxy_url, STR, set_proxy_url)},
121 { "max_conns", PTR(uzbl.net.max_conns, INT, cmd_max_conns)},
122 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, cmd_max_conns_host)},
123 { "useragent", PTR(uzbl.net.useragent, STR, cmd_useragent)},
124 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
125 }, *n2v_p = var_name_to_ptr;
131 { "SHIFT", GDK_SHIFT_MASK }, // shift
132 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
133 { "CONTROL", GDK_CONTROL_MASK }, // control
134 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
135 { "MOD2", GDK_MOD2_MASK }, // 5th mod
136 { "MOD3", GDK_MOD3_MASK }, // 6th mod
137 { "MOD4", GDK_MOD4_MASK }, // 7th mod
138 { "MOD5", GDK_MOD5_MASK }, // 8th mod
139 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
140 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
141 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
142 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
143 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
144 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
145 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
146 { "META", GDK_META_MASK }, // meta (since 2.10)
151 /* construct a hash from the var_name_to_ptr array for quick access */
153 make_var_to_name_hash() {
154 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
156 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
162 /* --- UTILITY FUNCTIONS --- */
168 snprintf(tmp, sizeof(tmp), "%i", val);
169 return g_strdup(tmp);
173 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
176 str_replace (const char* search, const char* replace, const char* string) {
180 buf = g_strsplit (string, search, -1);
181 ret = g_strjoinv (replace, buf);
182 g_strfreev(buf); // somebody said this segfaults
188 read_file_by_line (gchar *path) {
189 GIOChannel *chan = NULL;
190 gchar *readbuf = NULL;
192 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
195 chan = g_io_channel_new_file(path, "r", NULL);
198 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
199 const gchar* val = g_strdup (readbuf);
200 g_array_append_val (lines, val);
205 g_io_channel_unref (chan);
207 fprintf(stderr, "File '%s' not be read.\n", path);
214 gchar* parseenv (char* string) {
215 extern char** environ;
216 gchar* tmpstr = NULL;
220 while (environ[i] != NULL) {
221 gchar** env = g_strsplit (environ[i], "=", 2);
222 gchar* envname = g_strconcat ("$", env[0], NULL);
224 if (g_strrstr (string, envname) != NULL) {
225 tmpstr = g_strdup(string);
227 string = str_replace(envname, env[1], tmpstr);
232 g_strfreev (env); // somebody said this breaks uzbl
240 setup_signal(int signr, sigfunc *shandler) {
241 struct sigaction nh, oh;
243 nh.sa_handler = shandler;
244 sigemptyset(&nh.sa_mask);
247 if(sigaction(signr, &nh, &oh) < 0)
255 if (uzbl.behave.fifo_dir)
256 unlink (uzbl.comm.fifo_path);
257 if (uzbl.behave.socket_dir)
258 unlink (uzbl.comm.socket_path);
260 g_free(uzbl.state.executable_path);
261 g_string_free(uzbl.state.keycmd, TRUE);
262 g_hash_table_destroy(uzbl.bindings);
263 g_hash_table_destroy(uzbl.behave.commands);
267 /* --- SIGNAL HANDLER --- */
270 catch_sigterm(int s) {
276 catch_sigint(int s) {
282 /* --- CALLBACKS --- */
285 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
288 (void) navigation_action;
289 (void) policy_decision;
291 const gchar* uri = webkit_network_request_get_uri (request);
292 if (uzbl.state.verbose)
293 printf("New window requested -> %s \n", uri);
294 new_window_load_uri(uri);
299 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
303 if (uzbl.state.selected_url != NULL) {
304 if (uzbl.state.verbose)
305 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
306 new_window_load_uri(uzbl.state.selected_url);
308 if (uzbl.state.verbose)
309 printf("New web view -> %s\n","Nothing to open, exiting");
315 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
318 if (uzbl.behave.download_handler) {
319 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
320 if (uzbl.state.verbose)
321 printf("Download -> %s\n",uri);
322 /* if urls not escaped, we may have to escape and quote uri before this call */
323 run_handler(uzbl.behave.download_handler, uri);
328 /* scroll a bar in a given direction */
330 scroll (GtkAdjustment* bar, GArray *argv) {
334 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
335 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
336 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
339 static void scroll_begin(WebKitWebView* page, GArray *argv) {
340 (void) page; (void) argv;
341 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
344 static void scroll_end(WebKitWebView* page, GArray *argv) {
345 (void) page; (void) argv;
346 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
347 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
350 static void scroll_vert(WebKitWebView* page, GArray *argv) {
352 scroll(uzbl.gui.bar_v, argv);
355 static void scroll_horz(WebKitWebView* page, GArray *argv) {
357 scroll(uzbl.gui.bar_h, argv);
362 if (!uzbl.behave.show_status) {
363 gtk_widget_hide(uzbl.gui.mainbar);
365 gtk_widget_show(uzbl.gui.mainbar);
371 toggle_status_cb (WebKitWebView* page, GArray *argv) {
375 if (uzbl.behave.show_status) {
376 gtk_widget_hide(uzbl.gui.mainbar);
378 gtk_widget_show(uzbl.gui.mainbar);
380 uzbl.behave.show_status = !uzbl.behave.show_status;
385 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
389 //Set selected_url state variable
390 g_free(uzbl.state.selected_url);
391 uzbl.state.selected_url = NULL;
393 uzbl.state.selected_url = g_strdup(link);
399 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
403 if (uzbl.gui.main_title)
404 g_free (uzbl.gui.main_title);
405 uzbl.gui.main_title = g_strdup (title);
410 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
413 uzbl.gui.sbar.load_progress = progress;
418 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
422 if (uzbl.behave.load_finish_handler)
423 run_handler(uzbl.behave.load_finish_handler, "");
427 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
431 if (uzbl.behave.load_start_handler)
432 run_handler(uzbl.behave.load_start_handler, "");
436 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
439 g_free (uzbl.state.uri);
440 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
441 uzbl.state.uri = g_string_free (newuri, FALSE);
442 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
443 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
446 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
447 if (uzbl.behave.load_commit_handler)
448 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
452 destroy_cb (GtkWidget* widget, gpointer data) {
460 if (uzbl.behave.history_handler) {
462 struct tm * timeinfo;
465 timeinfo = localtime ( &rawtime );
466 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
467 run_handler(uzbl.behave.history_handler, date);
472 /* VIEW funcs (little webkit wrappers) */
473 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
475 VIEWFUNC(reload_bypass_cache)
476 VIEWFUNC(stop_loading)
483 /* -- command to callback/function map for things we cannot attach to any signals */
485 static struct {char *name; Command command[2];} cmdlist[] =
486 { /* key function no_split */
487 { "back", {view_go_back, 0} },
488 { "forward", {view_go_forward, 0} },
489 { "scroll_vert", {scroll_vert, 0} },
490 { "scroll_horz", {scroll_horz, 0} },
491 { "scroll_begin", {scroll_begin, 0} },
492 { "scroll_end", {scroll_end, 0} },
493 { "reload", {view_reload, 0}, },
494 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
495 { "stop", {view_stop_loading, 0}, },
496 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
497 { "zoom_out", {view_zoom_out, 0}, },
498 { "uri", {load_uri, NOSPLIT} },
499 { "js", {run_js, NOSPLIT} },
500 { "script", {run_external_js, 0} },
501 { "toggle_status", {toggle_status_cb, 0} },
502 { "spawn", {spawn, 0} },
503 { "sh", {spawn_sh, 0} },
504 { "exit", {close_uzbl, 0} },
505 { "search", {search_forward_text, NOSPLIT} },
506 { "search_reverse", {search_reverse_text, NOSPLIT} },
507 { "toggle_insert_mode", {toggle_insert_mode, 0} },
508 { "runcmd", {runcmd, NOSPLIT} }
515 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
517 for (i = 0; i < LENGTH(cmdlist); i++)
518 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
521 /* -- CORE FUNCTIONS -- */
524 free_action(gpointer act) {
525 Action *action = (Action*)act;
526 g_free(action->name);
528 g_free(action->param);
533 new_action(const gchar *name, const gchar *param) {
534 Action *action = g_new(Action, 1);
536 action->name = g_strdup(name);
538 action->param = g_strdup(param);
540 action->param = NULL;
546 file_exists (const char * filename) {
547 return (access(filename, F_OK) == 0);
551 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
554 if (argv_idx(argv, 0)) {
555 if (strcmp (argv_idx(argv, 0), "0") == 0) {
556 uzbl.behave.insert_mode = FALSE;
558 uzbl.behave.insert_mode = TRUE;
561 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
568 load_uri (WebKitWebView *web_view, GArray *argv) {
569 if (argv_idx(argv, 0)) {
570 GString* newuri = g_string_new (argv_idx(argv, 0));
571 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
572 g_string_prepend (newuri, "http://");
573 /* if we do handle cookies, ask our handler for them */
574 webkit_web_view_load_uri (web_view, newuri->str);
575 g_string_free (newuri, TRUE);
580 run_js (WebKitWebView * web_view, GArray *argv) {
581 if (argv_idx(argv, 0))
582 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
586 run_external_js (WebKitWebView * web_view, GArray *argv) {
587 if (argv_idx(argv, 0)) {
588 GArray* lines = read_file_by_line (argv_idx (argv, 0));
593 while ((line = g_array_index(lines, gchar*, i))) {
595 js = g_strdup (line);
597 gchar* newjs = g_strconcat (js, line, NULL);
604 if (uzbl.state.verbose)
605 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
607 if (argv_idx (argv, 1)) {
608 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
612 webkit_web_view_execute_script (web_view, js);
614 g_array_free (lines, TRUE);
619 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
620 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
621 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
622 webkit_web_view_unmark_text_matches (page);
623 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
624 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
628 if (uzbl.state.searchtx) {
629 if (uzbl.state.verbose)
630 printf ("Searching: %s\n", uzbl.state.searchtx);
631 webkit_web_view_set_highlight_text_matches (page, TRUE);
632 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
637 search_forward_text (WebKitWebView *page, GArray *argv) {
638 search_text(page, argv, TRUE);
642 search_reverse_text (WebKitWebView *page, GArray *argv) {
643 search_text(page, argv, FALSE);
647 new_window_load_uri (const gchar * uri) {
648 GString* to_execute = g_string_new ("");
649 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
651 for (i = 0; entries[i].long_name != NULL; i++) {
652 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
653 gchar** str = (gchar**)entries[i].arg_data;
655 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
659 if (uzbl.state.verbose)
660 printf("\n%s\n", to_execute->str);
661 g_spawn_command_line_async (to_execute->str, NULL);
662 g_string_free (to_execute, TRUE);
666 close_uzbl (WebKitWebView *page, GArray *argv) {
672 /* --Statusbar functions-- */
674 build_progressbar_ascii(int percent) {
675 int width=uzbl.gui.sbar.progress_w;
678 GString *bar = g_string_new("");
680 l = (double)percent*((double)width/100.);
681 l = (int)(l+.5)>=(int)l ? l+.5 : l;
683 for(i=0; i<(int)l; i++)
684 g_string_append(bar, uzbl.gui.sbar.progress_s);
687 g_string_append(bar, uzbl.gui.sbar.progress_u);
689 return g_string_free(bar, FALSE);
694 const GScannerConfig scan_config = {
697 ) /* cset_skip_characters */,
702 ) /* cset_identifier_first */,
709 ) /* cset_identifier_nth */,
710 ( "" ) /* cpair_comment_single */,
712 TRUE /* case_sensitive */,
714 FALSE /* skip_comment_multi */,
715 FALSE /* skip_comment_single */,
716 FALSE /* scan_comment_multi */,
717 TRUE /* scan_identifier */,
718 TRUE /* scan_identifier_1char */,
719 FALSE /* scan_identifier_NULL */,
720 TRUE /* scan_symbols */,
721 FALSE /* scan_binary */,
722 FALSE /* scan_octal */,
723 FALSE /* scan_float */,
724 FALSE /* scan_hex */,
725 FALSE /* scan_hex_dollar */,
726 FALSE /* scan_string_sq */,
727 FALSE /* scan_string_dq */,
728 TRUE /* numbers_2_int */,
729 FALSE /* int_2_float */,
730 FALSE /* identifier_2_string */,
731 FALSE /* char_2_token */,
732 FALSE /* symbol_2_token */,
733 TRUE /* scope_0_fallback */,
738 uzbl.scan = g_scanner_new(&scan_config);
739 while(symp->symbol_name) {
740 g_scanner_scope_add_symbol(uzbl.scan, 0,
742 GINT_TO_POINTER(symp->symbol_token));
748 expand_template(const char *template, gboolean escape_markup) {
749 if(!template) return NULL;
751 GTokenType token = G_TOKEN_NONE;
752 GString *ret = g_string_new("");
756 g_scanner_input_text(uzbl.scan, template, strlen(template));
757 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
758 token = g_scanner_get_next_token(uzbl.scan);
760 if(token == G_TOKEN_SYMBOL) {
761 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
765 buf = uzbl.state.uri?
766 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
767 g_string_append(ret, buf);
771 g_string_append(ret, uzbl.state.uri?
772 uzbl.state.uri:g_strdup(""));
775 buf = itos(uzbl.gui.sbar.load_progress);
776 g_string_append(ret, buf);
779 case SYM_LOADPRGSBAR:
780 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
781 g_string_append(ret, buf);
786 buf = uzbl.gui.main_title?
787 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
788 g_string_append(ret, buf);
792 g_string_append(ret, uzbl.gui.main_title?
793 uzbl.gui.main_title:g_strdup(""));
795 case SYM_SELECTED_URI:
797 buf = uzbl.state.selected_url?
798 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
799 g_string_append(ret, buf);
803 g_string_append(ret, uzbl.state.selected_url?
804 uzbl.state.selected_url:g_strdup(""));
807 buf = itos(uzbl.xwin);
809 uzbl.state.instance_name?uzbl.state.instance_name:buf);
814 buf = uzbl.state.keycmd->str?
815 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
816 g_string_append(ret, buf);
820 g_string_append(ret, uzbl.state.keycmd->str?
821 uzbl.state.keycmd->str:g_strdup(""));
825 uzbl.behave.insert_mode?"[I]":"[C]");
829 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
833 buf = itos(WEBKIT_MAJOR_VERSION);
834 g_string_append(ret, buf);
838 buf = itos(WEBKIT_MINOR_VERSION);
839 g_string_append(ret, buf);
843 buf = itos(WEBKIT_MICRO_VERSION);
844 g_string_append(ret, buf);
848 g_string_append(ret, uzbl.state.unameinfo.sysname);
851 g_string_append(ret, uzbl.state.unameinfo.nodename);
854 g_string_append(ret, uzbl.state.unameinfo.release);
857 g_string_append(ret, uzbl.state.unameinfo.version);
860 g_string_append(ret, uzbl.state.unameinfo.machine);
863 g_string_append(ret, ARCH);
867 g_string_append(ret, uzbl.state.unameinfo.domainname);
871 g_string_append(ret, COMMIT);
877 else if(token == G_TOKEN_INT) {
878 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
879 g_string_append(ret, buf);
882 else if(token == G_TOKEN_IDENTIFIER) {
883 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
885 else if(token == G_TOKEN_CHAR) {
886 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
890 return g_string_free(ret, FALSE);
892 /* --End Statusbar functions-- */
895 sharg_append(GArray *a, const gchar *str) {
896 const gchar *s = (str ? str : "");
897 g_array_append_val(a, s);
900 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
902 run_command (const gchar *command, const guint npre, const gchar **args,
903 const gboolean sync, char **stdout) {
904 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
907 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
908 gchar *pid = itos(getpid());
909 gchar *xwin = itos(uzbl.xwin);
911 sharg_append(a, command);
912 for (i = 0; i < npre; i++) /* add n args before the default vars */
913 sharg_append(a, args[i]);
914 sharg_append(a, uzbl.state.config_file);
915 sharg_append(a, pid);
916 sharg_append(a, xwin);
917 sharg_append(a, uzbl.comm.fifo_path);
918 sharg_append(a, uzbl.comm.socket_path);
919 sharg_append(a, uzbl.state.uri);
920 sharg_append(a, uzbl.gui.main_title);
922 for (i = npre; i < g_strv_length((gchar**)args); i++)
923 sharg_append(a, args[i]);
926 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
927 NULL, NULL, stdout, NULL, NULL, &err);
928 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
929 NULL, NULL, NULL, &err);
931 if (uzbl.state.verbose) {
932 GString *s = g_string_new("spawned:");
933 for (i = 0; i < (a->len); i++) {
934 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
935 g_string_append_printf(s, " %s", qarg);
938 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
939 printf("%s\n", s->str);
940 g_string_free(s, TRUE);
943 g_printerr("error on run_command: %s\n", err->message);
948 g_array_free (a, TRUE);
953 split_quoted(const gchar* src, const gboolean unquote) {
954 /* split on unquoted space, return array of strings;
955 remove a layer of quotes and backslashes if unquote */
956 if (!src) return NULL;
960 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
961 GString *s = g_string_new ("");
965 for (p = src; *p != '\0'; p++) {
966 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
967 else if (*p == '\\') { g_string_append_c(s, *p++);
968 g_string_append_c(s, *p); }
969 else if ((*p == '"') && unquote && !sq) dq = !dq;
970 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
972 else if ((*p == '\'') && unquote && !dq) sq = !sq;
973 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
975 else if ((*p == ' ') && !dq && !sq) {
976 dup = g_strdup(s->str);
977 g_array_append_val(a, dup);
978 g_string_truncate(s, 0);
979 } else g_string_append_c(s, *p);
981 dup = g_strdup(s->str);
982 g_array_append_val(a, dup);
983 ret = (gchar**)a->data;
984 g_array_free (a, FALSE);
985 g_string_free (s, TRUE);
990 spawn(WebKitWebView *web_view, GArray *argv) {
992 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
993 if (argv_idx(argv, 0))
994 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
998 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1000 if (!uzbl.behave.shell_cmd) {
1001 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1006 gchar *spacer = g_strdup("");
1007 g_array_insert_val(argv, 1, spacer);
1008 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1010 for (i = 1; i < g_strv_length(cmd); i++)
1011 g_array_prepend_val(argv, cmd[i]);
1013 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1019 parse_command(const char *cmd, const char *param) {
1022 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1025 gchar **par = split_quoted(param, TRUE);
1026 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1028 if (c[1] == NOSPLIT) { /* don't split */
1029 sharg_append(a, param);
1031 for (i = 0; i < g_strv_length(par); i++)
1032 sharg_append(a, par[i]);
1034 c[0](uzbl.gui.web_view, a);
1036 g_array_free (a, TRUE);
1039 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1042 /* command parser */
1045 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1046 G_REGEX_OPTIMIZE, 0, NULL);
1047 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1048 G_REGEX_OPTIMIZE, 0, NULL);
1049 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1050 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1051 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1052 G_REGEX_OPTIMIZE, 0, NULL);
1053 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1054 G_REGEX_OPTIMIZE, 0, NULL);
1058 get_var_value(gchar *name) {
1061 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1062 if(c->type == TYPE_STR)
1063 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1064 else if(c->type == TYPE_INT)
1065 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1074 if(*uzbl.net.proxy_url == ' '
1075 || uzbl.net.proxy_url == NULL) {
1076 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1077 (GType) SOUP_SESSION_PROXY_URI);
1080 suri = soup_uri_new(uzbl.net.proxy_url);
1081 g_object_set(G_OBJECT(uzbl.net.soup_session),
1082 SOUP_SESSION_PROXY_URI,
1084 soup_uri_free(suri);
1091 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1092 g_array_append_val (a, uzbl.state.uri);
1093 load_uri(uzbl.gui.web_view, a);
1094 g_array_free (a, TRUE);
1098 cmd_always_insert_mode() {
1099 uzbl.behave.insert_mode =
1100 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1106 g_object_set(G_OBJECT(uzbl.net.soup_session),
1107 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1111 cmd_max_conns_host() {
1112 g_object_set(G_OBJECT(uzbl.net.soup_session),
1113 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1118 soup_session_remove_feature
1119 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1120 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1121 /*g_free(uzbl.net.soup_logger);*/
1123 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1124 soup_session_add_feature(uzbl.net.soup_session,
1125 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1130 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1131 if (uzbl.behave.font_size > 0) {
1132 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1135 if (uzbl.behave.monospace_size > 0) {
1136 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1137 uzbl.behave.monospace_size, NULL);
1139 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1140 uzbl.behave.font_size, NULL);
1145 cmd_disable_plugins() {
1146 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1147 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1151 cmd_minimum_font_size() {
1152 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1153 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1158 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1163 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1171 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1172 uzbl.behave.modmask = 0;
1174 if(uzbl.behave.modkey)
1175 g_free(uzbl.behave.modkey);
1176 uzbl.behave.modkey = buf;
1178 for (i = 0; modkeys[i].key != NULL; i++) {
1179 if (g_strrstr(buf, modkeys[i].key))
1180 uzbl.behave.modmask |= modkeys[i].mask;
1186 if (*uzbl.net.useragent == ' ') {
1187 g_free (uzbl.net.useragent);
1188 uzbl.net.useragent = NULL;
1190 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1192 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1193 g_free(uzbl.net.useragent);
1194 uzbl.net.useragent = ua;
1200 gtk_widget_ref(uzbl.gui.scrolled_win);
1201 gtk_widget_ref(uzbl.gui.mainbar);
1202 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1203 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1205 if(uzbl.behave.status_top) {
1206 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1207 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1210 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1211 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1213 gtk_widget_unref(uzbl.gui.scrolled_win);
1214 gtk_widget_unref(uzbl.gui.mainbar);
1215 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1220 set_var_value(gchar *name, gchar *val) {
1221 uzbl_cmdprop *c = NULL;
1224 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1225 /* check for the variable type */
1226 if (c->type == TYPE_STR) {
1228 *c->ptr = g_strdup(val);
1229 } else if(c->type == TYPE_INT) {
1230 int *ip = GPOINTER_TO_INT(c->ptr);
1231 *ip = (int)strtoul(val, &endp, 10);
1234 /* invoke a command specific function */
1235 if(c->func) c->func();
1241 runcmd(WebKitWebView* page, GArray *argv) {
1243 parse_cmd_line(argv_idx(argv, 0));
1247 parse_cmd_line(const char *ctl_line) {
1248 gchar **tokens = NULL;
1251 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1252 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1253 if(tokens[0][0] == 0) {
1254 gchar* value = parseenv(g_strdup(tokens[2]));
1255 set_var_value(tokens[1], value);
1259 printf("Error in command: %s\n", tokens[0]);
1262 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1263 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1264 if(tokens[0][0] == 0) {
1265 get_var_value(tokens[1]);
1268 printf("Error in command: %s\n", tokens[0]);
1271 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1272 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1273 if(tokens[0][0] == 0) {
1274 gchar* value = parseenv(g_strdup(tokens[2]));
1275 add_binding(tokens[1], value);
1279 printf("Error in command: %s\n", tokens[0]);
1282 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1283 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1284 if(tokens[0][0] == 0) {
1285 parse_command(tokens[1], tokens[2]);
1288 printf("Error in command: %s\n", tokens[0]);
1290 /* KEYCMD command */
1291 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1292 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1293 if(tokens[0][0] == 0) {
1294 /* should incremental commands want each individual "keystroke"
1295 sent in a loop or the whole string in one go like now? */
1296 g_string_assign(uzbl.state.keycmd, tokens[1]);
1298 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1304 else if( (ctl_line[0] == '#')
1305 || (ctl_line[0] == ' ')
1306 || (ctl_line[0] == '\n'))
1307 ; /* ignore these lines */
1309 printf("Command not understood (%s)\n", ctl_line);
1318 build_stream_name(int type, const gchar* dir) {
1320 State *s = &uzbl.state;
1323 xwin_str = itos((int)uzbl.xwin);
1325 str = g_strdup_printf
1326 ("%s/uzbl_fifo_%s", dir,
1327 s->instance_name ? s->instance_name : xwin_str);
1328 } else if (type == SOCKET) {
1329 str = g_strdup_printf
1330 ("%s/uzbl_socket_%s", dir,
1331 s->instance_name ? s->instance_name : xwin_str );
1338 control_fifo(GIOChannel *gio, GIOCondition condition) {
1339 if (uzbl.state.verbose)
1340 printf("triggered\n");
1345 if (condition & G_IO_HUP)
1346 g_error ("Fifo: Read end of pipe died!\n");
1349 g_error ("Fifo: GIOChannel broke\n");
1351 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1352 if (ret == G_IO_STATUS_ERROR) {
1353 g_error ("Fifo: Error reading: %s\n", err->message);
1357 parse_cmd_line(ctl_line);
1364 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1365 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1366 if (unlink(uzbl.comm.fifo_path) == -1)
1367 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1368 g_free(uzbl.comm.fifo_path);
1369 uzbl.comm.fifo_path = NULL;
1372 if (*dir == ' ') { /* space unsets the variable */
1377 GIOChannel *chan = NULL;
1378 GError *error = NULL;
1379 gchar *path = build_stream_name(FIFO, dir);
1381 if (!file_exists(path)) {
1382 if (mkfifo (path, 0666) == 0) {
1383 // 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.
1384 chan = g_io_channel_new_file(path, "r+", &error);
1386 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1387 if (uzbl.state.verbose)
1388 printf ("init_fifo: created successfully as %s\n", path);
1389 uzbl.comm.fifo_path = path;
1391 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1392 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1393 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1394 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1396 /* if we got this far, there was an error; cleanup */
1397 if (error) g_error_free (error);
1404 control_stdin(GIOChannel *gio, GIOCondition condition) {
1406 gchar *ctl_line = NULL;
1409 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1410 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1413 parse_cmd_line(ctl_line);
1421 GIOChannel *chan = NULL;
1422 GError *error = NULL;
1424 chan = g_io_channel_unix_new(fileno(stdin));
1426 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1427 g_error ("Stdin: could not add watch\n");
1429 if (uzbl.state.verbose)
1430 printf ("Stdin: watch added successfully\n");
1433 g_error ("Stdin: Error while opening: %s\n", error->message);
1435 if (error) g_error_free (error);
1439 control_socket(GIOChannel *chan) {
1440 struct sockaddr_un remote;
1441 char buffer[512], *ctl_line;
1443 int sock, clientsock, n, done;
1446 sock = g_io_channel_unix_get_fd(chan);
1448 memset (buffer, 0, sizeof (buffer));
1450 t = sizeof (remote);
1451 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1455 memset (temp, 0, sizeof (temp));
1456 n = recv (clientsock, temp, 128, 0);
1458 buffer[strlen (buffer)] = '\0';
1462 strcat (buffer, temp);
1465 if (strcmp (buffer, "\n") < 0) {
1466 buffer[strlen (buffer) - 1] = '\0';
1468 buffer[strlen (buffer)] = '\0';
1471 ctl_line = g_strdup(buffer);
1472 parse_cmd_line (ctl_line);
1475 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1476 GError *error = NULL;
1479 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1480 if (ret == G_IO_STATUS_ERROR)
1481 g_error ("Error reading: %s\n", error->message);
1483 printf("Got line %s (%u bytes) \n",ctl_line, len);
1485 parse_line(ctl_line);
1493 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1494 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1495 if (unlink(uzbl.comm.socket_path) == -1)
1496 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1497 g_free(uzbl.comm.socket_path);
1498 uzbl.comm.socket_path = NULL;
1506 GIOChannel *chan = NULL;
1508 struct sockaddr_un local;
1509 gchar *path = build_stream_name(SOCKET, dir);
1511 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1513 local.sun_family = AF_UNIX;
1514 strcpy (local.sun_path, path);
1515 unlink (local.sun_path);
1517 len = strlen (local.sun_path) + sizeof (local.sun_family);
1518 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1519 if (uzbl.state.verbose)
1520 printf ("init_socket: opened in %s\n", path);
1523 if( (chan = g_io_channel_unix_new(sock)) ) {
1524 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1525 uzbl.comm.socket_path = path;
1528 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1530 /* if we got this far, there was an error; cleanup */
1537 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1538 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1540 // this function may be called very early when the templates are not set (yet), hence the checks
1542 update_title (void) {
1543 Behaviour *b = &uzbl.behave;
1546 if (b->show_status) {
1547 if (b->title_format_short) {
1548 parsed = expand_template(b->title_format_short, FALSE);
1549 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1552 if (b->status_format) {
1553 parsed = expand_template(b->status_format, TRUE);
1554 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1557 if (b->status_background) {
1559 gdk_color_parse (b->status_background, &color);
1560 //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)
1561 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1564 if (b->title_format_long) {
1565 parsed = expand_template(b->title_format_long, FALSE);
1566 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1573 key_press_cb (GtkWidget* window, GdkEventKey* event)
1575 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1579 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1580 || 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)
1583 /* turn off insert mode (if always_insert_mode is not used) */
1584 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1585 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1590 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1593 if (event->keyval == GDK_Escape) {
1594 g_string_truncate(uzbl.state.keycmd, 0);
1599 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1600 if (event->keyval == GDK_Insert) {
1602 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1603 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1605 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1608 g_string_append (uzbl.state.keycmd, str);
1615 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1616 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1620 gboolean key_ret = FALSE;
1621 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1623 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1625 run_keycmd(key_ret);
1627 if (key_ret) return (!uzbl.behave.insert_mode);
1632 run_keycmd(const gboolean key_ret) {
1633 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1635 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1636 g_string_truncate(uzbl.state.keycmd, 0);
1637 parse_command(action->name, action->param);
1641 /* try if it's an incremental keycmd or one that takes args, and run it */
1642 GString* short_keys = g_string_new ("");
1643 GString* short_keys_inc = g_string_new ("");
1645 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1646 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1647 g_string_assign(short_keys_inc, short_keys->str);
1648 g_string_append_c(short_keys, '_');
1649 g_string_append_c(short_keys_inc, '*');
1651 gboolean exec_now = FALSE;
1652 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1653 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1654 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1655 if (key_ret) { /* just quit the incremental command on return */
1656 g_string_truncate(uzbl.state.keycmd, 0);
1658 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1662 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1663 GString* actionname = g_string_new ("");
1664 GString* actionparam = g_string_new ("");
1665 g_string_erase (parampart, 0, i+1);
1667 g_string_printf (actionname, action->name, parampart->str);
1669 g_string_printf (actionparam, action->param, parampart->str);
1670 parse_command(actionname->str, actionparam->str);
1671 g_string_free (actionname, TRUE);
1672 g_string_free (actionparam, TRUE);
1673 g_string_free (parampart, TRUE);
1675 g_string_truncate(uzbl.state.keycmd, 0);
1679 g_string_truncate(short_keys, short_keys->len - 1);
1681 g_string_free (short_keys, TRUE);
1682 g_string_free (short_keys_inc, TRUE);
1689 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1690 //main_window_ref = g_object_ref(scrolled_window);
1691 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
1693 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1694 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1696 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1697 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1698 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1699 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1700 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1701 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1702 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1703 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1704 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1705 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1707 return scrolled_window;
1714 g->mainbar = gtk_hbox_new (FALSE, 0);
1716 /* keep a reference to the bar so we can re-pack it at runtime*/
1717 //sbar_ref = g_object_ref(g->mainbar);
1719 g->mainbar_label = gtk_label_new ("");
1720 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1721 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1722 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1723 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1724 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1729 GtkWidget* create_window () {
1730 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1731 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1732 gtk_widget_set_name (window, "Uzbl browser");
1733 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1734 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1740 run_handler (const gchar *act, const gchar *args) {
1741 char **parts = g_strsplit(act, " ", 2);
1743 else if ((g_strcmp0(parts[0], "spawn") == 0)
1744 || (g_strcmp0(parts[0], "sh") == 0)) {
1746 GString *a = g_string_new ("");
1748 spawnparts = split_quoted(parts[1], FALSE);
1749 g_string_append_printf(a, "%s", spawnparts[0]);
1750 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1751 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1752 g_string_append_printf(a, " %s", spawnparts[i]);
1753 parse_command(parts[0], a->str);
1754 g_string_free (a, TRUE);
1755 g_strfreev (spawnparts);
1757 parse_command(parts[0], parts[1]);
1762 add_binding (const gchar *key, const gchar *act) {
1763 char **parts = g_strsplit(act, " ", 2);
1770 if (uzbl.state.verbose)
1771 printf ("Binding %-10s : %s\n", key, act);
1772 action = new_action(parts[0], parts[1]);
1774 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1779 get_xdg_var (XDG_Var xdg) {
1780 const gchar* actual_value = getenv (xdg.environmental);
1781 const gchar* home = getenv ("HOME");
1783 gchar* return_value = str_replace ("~", home, actual_value);
1785 if (! actual_value || strcmp (actual_value, "") == 0) {
1786 if (xdg.default_value) {
1787 return_value = str_replace ("~", home, xdg.default_value);
1789 return_value = NULL;
1792 return return_value;
1796 find_xdg_file (int xdg_type, char* filename) {
1797 /* xdg_type = 0 => config
1798 xdg_type = 1 => data
1799 xdg_type = 2 => cache*/
1801 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1802 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1805 gchar* temporary_string;
1809 if (! file_exists (temporary_file) && xdg_type != 2) {
1810 buf = get_xdg_var (XDG[3 + xdg_type]);
1811 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1814 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1815 g_free (temporary_file);
1816 temporary_file = g_strconcat (temporary_string, filename, NULL);
1820 //g_free (temporary_string); - segfaults.
1822 if (file_exists (temporary_file)) {
1823 return temporary_file;
1830 State *s = &uzbl.state;
1831 Network *n = &uzbl.net;
1833 for (i = 0; default_config[i].command != NULL; i++) {
1834 parse_cmd_line(default_config[i].command);
1837 if (!s->config_file) {
1838 s->config_file = find_xdg_file (0, "/uzbl/config");
1841 if (s->config_file) {
1842 GArray* lines = read_file_by_line (s->config_file);
1846 while ((line = g_array_index(lines, gchar*, i))) {
1847 parse_cmd_line (line);
1851 g_array_free (lines, TRUE);
1853 if (uzbl.state.verbose)
1854 printf ("No configuration file loaded.\n");
1857 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1860 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1863 if (!uzbl.behave.cookie_handler) return;
1865 gchar * stdout = NULL;
1866 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1867 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1868 gchar *action = g_strdup ("GET");
1869 SoupURI * soup_uri = soup_message_get_uri(msg);
1870 sharg_append(a, action);
1871 sharg_append(a, soup_uri->host);
1872 sharg_append(a, soup_uri->path);
1873 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1874 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1876 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1879 g_array_free(a, TRUE);
1883 save_cookies (SoupMessage *msg, gpointer user_data){
1887 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1888 cookie = soup_cookie_to_set_cookie_header(ck->data);
1889 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1890 SoupURI * soup_uri = soup_message_get_uri(msg);
1891 gchar *action = strdup("PUT");
1892 sharg_append(a, action);
1893 sharg_append(a, soup_uri->host);
1894 sharg_append(a, soup_uri->path);
1895 sharg_append(a, cookie);
1896 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1899 g_array_free(a, TRUE);
1905 main (int argc, char* argv[]) {
1906 gtk_init (&argc, &argv);
1907 if (!g_thread_supported ())
1908 g_thread_init (NULL);
1909 uzbl.state.executable_path = g_strdup(argv[0]);
1910 uzbl.state.selected_url = NULL;
1911 uzbl.state.searchtx = NULL;
1913 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1914 g_option_context_add_main_entries (context, entries, NULL);
1915 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1916 g_option_context_parse (context, &argc, &argv, NULL);
1917 g_option_context_free(context);
1918 /* initialize hash table */
1919 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1921 uzbl.net.soup_session = webkit_get_default_session();
1922 uzbl.state.keycmd = g_string_new("");
1924 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1925 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1926 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1927 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1929 if(uname(&uzbl.state.unameinfo) == -1)
1930 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1932 uzbl.gui.sbar.progress_s = g_strdup("=");
1933 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1934 uzbl.gui.sbar.progress_w = 10;
1939 make_var_to_name_hash();
1941 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1943 uzbl.gui.scrolled_win = create_browser();
1946 /* initial packing */
1947 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1948 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1950 uzbl.gui.main_window = create_window ();
1951 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1954 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1955 gtk_widget_show_all (uzbl.gui.main_window);
1956 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1958 if (uzbl.state.verbose) {
1959 printf("Uzbl start location: %s\n", argv[0]);
1960 printf("window_id %i\n",(int) uzbl.xwin);
1961 printf("pid %i\n", getpid ());
1962 printf("name: %s\n", uzbl.state.instance_name);
1965 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1966 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1967 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1968 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1969 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1973 if (!uzbl.behave.show_status)
1974 gtk_widget_hide(uzbl.gui.mainbar);
1980 if(uzbl.state.uri) {
1981 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1982 g_array_append_val(a, uzbl.state.uri);
1983 load_uri (uzbl.gui.web_view, a);
1984 g_array_free (a, TRUE);
1990 return EXIT_SUCCESS;
1993 /* vi: set et ts=4: */