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_STRING};
85 } var_name_to_ptr[] = {
86 /* variable name pointer to variable in code variable type callback function */
87 /* ------------------------------------------------------------------------------------------------------------------- */
88 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
89 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
90 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
91 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
92 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
93 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
94 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
95 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
96 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
97 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
98 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
99 { "modkey", {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
100 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
101 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
102 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
103 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
104 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
105 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
106 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
107 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
108 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
109 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
110 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
111 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
112 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
113 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
114 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
115 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
116 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
117 }, *n2v_p = var_name_to_ptr;
123 { "SHIFT", GDK_SHIFT_MASK }, // shift
124 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
125 { "CONTROL", GDK_CONTROL_MASK }, // control
126 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
127 { "MOD2", GDK_MOD2_MASK }, // 5th mod
128 { "MOD3", GDK_MOD3_MASK }, // 6th mod
129 { "MOD4", GDK_MOD4_MASK }, // 7th mod
130 { "MOD5", GDK_MOD5_MASK }, // 8th mod
131 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
132 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
133 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
134 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
135 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
136 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
137 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
138 { "META", GDK_META_MASK }, // meta (since 2.10)
143 /* construct a hash from the var_name_to_ptr array for quick access */
145 make_var_to_name_hash() {
146 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
148 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
154 /* --- UTILITY FUNCTIONS --- */
160 snprintf(tmp, sizeof(tmp), "%i", val);
161 return g_strdup(tmp);
165 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
168 str_replace (const char* search, const char* replace, const char* string) {
172 buf = g_strsplit (string, search, -1);
173 ret = g_strjoinv (replace, buf);
180 setup_signal(int signr, sigfunc *shandler) {
181 struct sigaction nh, oh;
183 nh.sa_handler = shandler;
184 sigemptyset(&nh.sa_mask);
187 if(sigaction(signr, &nh, &oh) < 0)
195 if (uzbl.behave.fifo_dir)
196 unlink (uzbl.comm.fifo_path);
197 if (uzbl.behave.socket_dir)
198 unlink (uzbl.comm.socket_path);
200 g_free(uzbl.state.executable_path);
201 g_string_free(uzbl.state.keycmd, TRUE);
202 g_hash_table_destroy(uzbl.bindings);
203 g_hash_table_destroy(uzbl.behave.commands);
207 /* --- SIGNAL HANDLER --- */
210 catch_sigterm(int s) {
216 catch_sigint(int s) {
222 /* --- CALLBACKS --- */
225 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
228 (void) navigation_action;
229 (void) policy_decision;
231 const gchar* uri = webkit_network_request_get_uri (request);
232 if (uzbl.state.verbose)
233 printf("New window requested -> %s \n", uri);
234 new_window_load_uri(uri);
239 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
243 if (uzbl.state.selected_url != NULL) {
244 if (uzbl.state.verbose)
245 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
246 new_window_load_uri(uzbl.state.selected_url);
248 if (uzbl.state.verbose)
249 printf("New web view -> %s\n","Nothing to open, exiting");
255 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
258 if (uzbl.behave.download_handler) {
259 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
260 if (uzbl.state.verbose)
261 printf("Download -> %s\n",uri);
262 /* if urls not escaped, we may have to escape and quote uri before this call */
263 run_handler(uzbl.behave.download_handler, uri);
268 /* scroll a bar in a given direction */
270 scroll (GtkAdjustment* bar, GArray *argv) {
274 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
275 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
276 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
279 static void scroll_begin(WebKitWebView* page, GArray *argv) {
280 (void) page; (void) argv;
281 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
284 static void scroll_end(WebKitWebView* page, GArray *argv) {
285 (void) page; (void) argv;
286 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
287 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
290 static void scroll_vert(WebKitWebView* page, GArray *argv) {
292 scroll(uzbl.gui.bar_v, argv);
295 static void scroll_horz(WebKitWebView* page, GArray *argv) {
297 scroll(uzbl.gui.bar_h, argv);
302 if (!uzbl.behave.show_status) {
303 gtk_widget_hide(uzbl.gui.mainbar);
305 gtk_widget_show(uzbl.gui.mainbar);
311 toggle_status_cb (WebKitWebView* page, GArray *argv) {
315 if (uzbl.behave.show_status) {
316 gtk_widget_hide(uzbl.gui.mainbar);
318 gtk_widget_show(uzbl.gui.mainbar);
320 uzbl.behave.show_status = !uzbl.behave.show_status;
325 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
329 //Set selected_url state variable
330 g_free(uzbl.state.selected_url);
331 uzbl.state.selected_url = NULL;
333 uzbl.state.selected_url = g_strdup(link);
339 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
343 if (uzbl.gui.main_title)
344 g_free (uzbl.gui.main_title);
345 uzbl.gui.main_title = g_strdup (title);
350 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
353 uzbl.gui.sbar.load_progress = progress;
358 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
362 if (uzbl.behave.load_finish_handler)
363 run_handler(uzbl.behave.load_finish_handler, "");
367 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
371 if (uzbl.behave.load_start_handler)
372 run_handler(uzbl.behave.load_start_handler, "");
376 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
379 free (uzbl.state.uri);
380 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
381 uzbl.state.uri = g_string_free (newuri, FALSE);
382 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
383 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
386 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
387 if (uzbl.behave.load_commit_handler)
388 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
392 destroy_cb (GtkWidget* widget, gpointer data) {
400 if (uzbl.behave.history_handler) {
402 struct tm * timeinfo;
405 timeinfo = localtime ( &rawtime );
406 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
407 run_handler(uzbl.behave.history_handler, date);
412 /* VIEW funcs (little webkit wrappers) */
413 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
415 VIEWFUNC(reload_bypass_cache)
416 VIEWFUNC(stop_loading)
423 /* -- command to callback/function map for things we cannot attach to any signals */
426 static struct {char *name; Command command[2];} cmdlist[] =
427 { /* key function no_split */
428 { "back", {view_go_back, 0} },
429 { "forward", {view_go_forward, 0} },
430 { "scroll_vert", {scroll_vert, 0} },
431 { "scroll_horz", {scroll_horz, 0} },
432 { "scroll_begin", {scroll_begin, 0} },
433 { "scroll_end", {scroll_end, 0} },
434 { "reload", {view_reload, 0}, },
435 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
436 { "stop", {view_stop_loading, 0}, },
437 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
438 { "zoom_out", {view_zoom_out, 0}, },
439 { "uri", {load_uri, NOSPLIT} },
440 { "script", {run_js, NOSPLIT} },
441 { "toggle_status", {toggle_status_cb, 0} },
442 { "spawn", {spawn, 0} },
443 { "sh", {spawn_sh, 0} },
444 { "exit", {close_uzbl, 0} },
445 { "search", {search_forward_text, NOSPLIT} },
446 { "search_reverse", {search_reverse_text, NOSPLIT} },
447 { "insert_mode", {set_insert_mode, 0} },
448 { "runcmd", {runcmd, NOSPLIT} }
455 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
457 for (i = 0; i < LENGTH(cmdlist); i++)
458 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
461 /* -- CORE FUNCTIONS -- */
464 free_action(gpointer act) {
465 Action *action = (Action*)act;
466 g_free(action->name);
468 g_free(action->param);
473 new_action(const gchar *name, const gchar *param) {
474 Action *action = g_new(Action, 1);
476 action->name = g_strdup(name);
478 action->param = g_strdup(param);
480 action->param = NULL;
486 file_exists (const char * filename) {
487 return (access(filename, F_OK) == 0);
491 set_insert_mode(WebKitWebView *page, GArray *argv) {
495 uzbl.behave.insert_mode = TRUE;
500 load_uri (WebKitWebView *web_view, GArray *argv) {
501 if (argv_idx(argv, 0)) {
502 GString* newuri = g_string_new (argv_idx(argv, 0));
503 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
504 g_string_prepend (newuri, "http://");
505 /* if we do handle cookies, ask our handler for them */
506 webkit_web_view_load_uri (web_view, newuri->str);
507 g_string_free (newuri, TRUE);
512 run_js (WebKitWebView * web_view, GArray *argv) {
513 if (argv_idx(argv, 0))
514 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
518 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
519 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
520 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
522 if (uzbl.state.searchtx != NULL) {
523 if (uzbl.state.verbose)
524 printf ("Searching: %s\n", uzbl.state.searchtx);
525 webkit_web_view_unmark_text_matches (page);
526 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
527 webkit_web_view_set_highlight_text_matches (page, TRUE);
528 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
529 g_free(uzbl.state.searchtx);
530 uzbl.state.searchtx = NULL;
535 search_forward_text (WebKitWebView *page, GArray *argv) {
536 search_text(page, argv, TRUE);
540 search_reverse_text (WebKitWebView *page, GArray *argv) {
541 search_text(page, argv, FALSE);
545 new_window_load_uri (const gchar * uri) {
546 GString* to_execute = g_string_new ("");
547 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
549 for (i = 0; entries[i].long_name != NULL; i++) {
550 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
551 gchar** str = (gchar**)entries[i].arg_data;
553 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
557 if (uzbl.state.verbose)
558 printf("\n%s\n", to_execute->str);
559 g_spawn_command_line_async (to_execute->str, NULL);
560 g_string_free (to_execute, TRUE);
564 close_uzbl (WebKitWebView *page, GArray *argv) {
570 /* --Statusbar functions-- */
572 build_progressbar_ascii(int percent) {
576 GString *bar = g_string_new("");
578 l = (double)percent*((double)width/100.);
579 l = (int)(l+.5)>=(int)l ? l+.5 : l;
581 for(i=0; i<(int)l; i++)
582 g_string_append(bar, "=");
585 g_string_append(bar, "·");
587 return g_string_free(bar, FALSE);
592 const GScannerConfig scan_config = {
595 ) /* cset_skip_characters */,
600 ) /* cset_identifier_first */,
607 ) /* cset_identifier_nth */,
608 ( "" ) /* cpair_comment_single */,
610 TRUE /* case_sensitive */,
612 FALSE /* skip_comment_multi */,
613 FALSE /* skip_comment_single */,
614 FALSE /* scan_comment_multi */,
615 TRUE /* scan_identifier */,
616 TRUE /* scan_identifier_1char */,
617 FALSE /* scan_identifier_NULL */,
618 TRUE /* scan_symbols */,
619 FALSE /* scan_binary */,
620 FALSE /* scan_octal */,
621 FALSE /* scan_float */,
622 FALSE /* scan_hex */,
623 FALSE /* scan_hex_dollar */,
624 FALSE /* scan_string_sq */,
625 FALSE /* scan_string_dq */,
626 TRUE /* numbers_2_int */,
627 FALSE /* int_2_float */,
628 FALSE /* identifier_2_string */,
629 FALSE /* char_2_token */,
630 FALSE /* symbol_2_token */,
631 TRUE /* scope_0_fallback */,
636 uzbl.scan = g_scanner_new(&scan_config);
637 while(symp->symbol_name) {
638 g_scanner_scope_add_symbol(uzbl.scan, 0,
640 GINT_TO_POINTER(symp->symbol_token));
646 expand_template(const char *template) {
647 if(!template) return NULL;
649 GTokenType token = G_TOKEN_NONE;
650 GString *ret = g_string_new("");
654 g_scanner_input_text(uzbl.scan, template, strlen(template));
655 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
656 token = g_scanner_get_next_token(uzbl.scan);
658 if(token == G_TOKEN_SYMBOL) {
659 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
662 buf = uzbl.state.uri?
663 g_markup_printf_escaped("%s", uzbl.state.uri) :
665 g_string_append(ret, buf);
669 buf = itos(uzbl.gui.sbar.load_progress);
670 g_string_append(ret, buf);
673 case SYM_LOADPRGSBAR:
674 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
675 g_string_append(ret, buf);
679 buf = uzbl.gui.main_title?
680 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
682 g_string_append(ret, buf);
685 case SYM_SELECTED_URI:
686 buf = uzbl.state.selected_url?
687 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
689 g_string_append(ret, buf);
693 buf = itos(uzbl.xwin);
695 uzbl.state.instance_name?uzbl.state.instance_name:buf);
699 buf = uzbl.state.keycmd->str?
700 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
702 g_string_append(ret, buf);
707 uzbl.behave.insert_mode?"[I]":"[C]");
711 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
715 buf = itos(WEBKIT_MAJOR_VERSION);
716 g_string_append(ret, buf);
720 buf = itos(WEBKIT_MINOR_VERSION);
721 g_string_append(ret, buf);
725 buf = itos(WEBKIT_MICRO_VERSION);
726 g_string_append(ret, buf);
730 g_string_append(ret, uzbl.state.unameinfo.sysname);
733 g_string_append(ret, uzbl.state.unameinfo.nodename);
736 g_string_append(ret, uzbl.state.unameinfo.release);
739 g_string_append(ret, uzbl.state.unameinfo.version);
742 g_string_append(ret, uzbl.state.unameinfo.machine);
745 g_string_append(ret, ARCH);
749 g_string_append(ret, uzbl.state.unameinfo.domainname);
753 g_string_append(ret, COMMIT);
759 else if(token == G_TOKEN_INT) {
760 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
761 g_string_append(ret, buf);
764 else if(token == G_TOKEN_IDENTIFIER) {
765 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
767 else if(token == G_TOKEN_CHAR) {
768 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
772 return g_string_free(ret, FALSE);
774 /* --End Statusbar functions-- */
777 sharg_append(GArray *a, const gchar *str) {
778 const gchar *s = (str ? str : "");
779 g_array_append_val(a, s);
782 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
784 run_command (const gchar *command, const guint npre, const gchar **args,
785 const gboolean sync, char **stdout) {
786 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
789 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
790 gchar *pid = itos(getpid());
791 gchar *xwin = itos(uzbl.xwin);
793 sharg_append(a, command);
794 for (i = 0; i < npre; i++) /* add n args before the default vars */
795 sharg_append(a, args[i]);
796 sharg_append(a, uzbl.state.config_file);
797 sharg_append(a, pid);
798 sharg_append(a, xwin);
799 sharg_append(a, uzbl.comm.fifo_path);
800 sharg_append(a, uzbl.comm.socket_path);
801 sharg_append(a, uzbl.state.uri);
802 sharg_append(a, uzbl.gui.main_title);
804 for (i = npre; i < g_strv_length((gchar**)args); i++)
805 sharg_append(a, args[i]);
807 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
808 NULL, NULL, stdout, NULL, NULL, &err);
809 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
810 NULL, NULL, NULL, &err);
812 if (uzbl.state.verbose) {
813 GString *s = g_string_new("spawned:");
814 for (i = 0; i < (a->len); i++) {
815 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
816 g_string_append_printf(s, " %s", qarg);
819 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
820 printf("%s\n", s->str);
821 g_string_free(s, TRUE);
824 g_printerr("error on run_command: %s\n", err->message);
829 g_array_free (a, TRUE);
834 split_quoted(const gchar* src, const gboolean unquote) {
835 /* split on unquoted space, return array of strings;
836 remove a layer of quotes and backslashes if unquote */
837 if (!src) return NULL;
841 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
842 GString *s = g_string_new ("");
846 for (p = src; *p != '\0'; p++) {
847 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
848 else if (*p == '\\') { g_string_append_c(s, *p++);
849 g_string_append_c(s, *p); }
850 else if ((*p == '"') && unquote && !sq) dq = !dq;
851 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
853 else if ((*p == '\'') && unquote && !dq) sq = !sq;
854 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
856 else if ((*p == ' ') && !dq && !sq) {
857 dup = g_strdup(s->str);
858 g_array_append_val(a, dup);
859 g_string_truncate(s, 0);
860 } else g_string_append_c(s, *p);
862 dup = g_strdup(s->str);
863 g_array_append_val(a, dup);
864 ret = (gchar**)a->data;
865 g_array_free (a, FALSE);
866 g_string_free (s, FALSE);
871 spawn(WebKitWebView *web_view, GArray *argv) {
873 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
874 if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, argv->data + sizeof(gchar*), FALSE, NULL);
878 spawn_sh(WebKitWebView *web_view, GArray *argv) {
880 if (!uzbl.behave.shell_cmd) {
881 g_printerr ("spawn_sh: shell_cmd is not set!\n");
886 gchar *spacer = g_strdup("");
887 g_array_insert_val(argv, 1, spacer);
888 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
890 for (i = 1; i < g_strv_length(cmd); i++)
891 g_array_prepend_val(argv, cmd[i]);
893 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, argv->data, FALSE, NULL);
899 parse_command(const char *cmd, const char *param) {
902 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
905 gchar **par = split_quoted(param, TRUE);
906 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
908 if (c[1]) { /* don't split */
909 sharg_append(a, param);
911 for (i = 0; i < g_strv_length(par); i++)
912 sharg_append(a, par[i]);
914 c[0](uzbl.gui.web_view, a);
916 g_array_free (a, TRUE);
919 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
925 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
926 G_REGEX_OPTIMIZE, 0, NULL);
927 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
928 G_REGEX_OPTIMIZE, 0, NULL);
929 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
930 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
931 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
932 G_REGEX_OPTIMIZE, 0, NULL);
933 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
934 G_REGEX_OPTIMIZE, 0, NULL);
938 get_var_value(gchar *name) {
941 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
942 if(c->type == TYPE_STRING)
943 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
944 else if(c->type == TYPE_INT)
945 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
954 if(*uzbl.net.proxy_url == ' '
955 || uzbl.net.proxy_url == NULL) {
956 soup_session_remove_feature_by_type(uzbl.net.soup_session,
957 (GType) SOUP_SESSION_PROXY_URI);
960 suri = soup_uri_new(uzbl.net.proxy_url);
961 g_object_set(G_OBJECT(uzbl.net.soup_session),
962 SOUP_SESSION_PROXY_URI,
971 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
972 g_array_append_val (a, uzbl.state.uri);
973 load_uri(uzbl.gui.web_view, a);
974 g_array_free (a, TRUE);
978 cmd_always_insert_mode() {
979 uzbl.behave.insert_mode =
980 uzbl.behave.always_insert_mode ? TRUE : FALSE;
986 g_object_set(G_OBJECT(uzbl.net.soup_session),
987 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
991 cmd_max_conns_host() {
992 g_object_set(G_OBJECT(uzbl.net.soup_session),
993 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
998 soup_session_remove_feature
999 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1000 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1001 /*g_free(uzbl.net.soup_logger);*/
1003 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1004 soup_session_add_feature(uzbl.net.soup_session,
1005 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1009 cmd_default_font_size() {
1010 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1011 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1015 cmd_minimum_font_size() {
1016 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1017 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1024 buf = init_fifo(uzbl.behave.fifo_dir);
1025 if(uzbl.behave.fifo_dir)
1026 free(uzbl.behave.fifo_dir);
1028 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1035 buf = init_socket(uzbl.behave.fifo_dir);
1036 if(uzbl.behave.socket_dir)
1037 free(uzbl.behave.socket_dir);
1039 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1047 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1048 uzbl.behave.modmask = 0;
1050 if(uzbl.behave.modkey)
1051 free(uzbl.behave.modkey);
1052 uzbl.behave.modkey = buf;
1054 for (i = 0; modkeys[i].key != NULL; i++) {
1055 if (g_strrstr(uzbl.behave.modkey, modkeys[i].key))
1056 uzbl.behave.modmask |= modkeys[i].mask;
1064 buf = set_useragent(uzbl.net.useragent);
1065 if(uzbl.net.useragent)
1066 free(uzbl.net.useragent);
1068 uzbl.net.useragent = buf?buf:g_strdup("");
1073 gtk_widget_ref(uzbl.gui.scrolled_win);
1074 gtk_widget_ref(uzbl.gui.mainbar);
1075 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1076 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1078 if(uzbl.behave.status_top) {
1079 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1080 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1083 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1084 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1086 gtk_widget_unref(uzbl.gui.scrolled_win);
1087 gtk_widget_unref(uzbl.gui.mainbar);
1088 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1093 set_var_value(gchar *name, gchar *val) {
1094 uzbl_cmdprop *c = NULL;
1097 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1098 /* check for the variable type */
1099 if (c->type == TYPE_STRING) {
1101 *c->ptr = g_strdup(val);
1102 } else if(c->type == TYPE_INT) {
1103 int *ip = GPOINTER_TO_INT(c->ptr);
1104 *ip = (int)strtoul(val, &endp, 10);
1107 /* invoke a command specific function */
1108 if(c->func) c->func();
1114 runcmd(WebKitWebView* page, GArray *argv) {
1116 parse_cmd_line(argv_idx(argv, 0));
1120 parse_cmd_line(const char *ctl_line) {
1124 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1125 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1126 if(tokens[0][0] == 0) {
1127 set_var_value(tokens[1], tokens[2]);
1131 printf("Error in command: %s\n", tokens[0]);
1134 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1135 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1136 if(tokens[0][0] == 0) {
1137 get_var_value(tokens[1]);
1141 printf("Error in command: %s\n", tokens[0]);
1144 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1145 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1146 if(tokens[0][0] == 0) {
1147 add_binding(tokens[1], tokens[2]);
1151 printf("Error in command: %s\n", tokens[0]);
1154 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1155 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1156 if(tokens[0][0] == 0) {
1157 parse_command(tokens[1], tokens[2]);
1161 printf("Error in command: %s\n", tokens[0]);
1163 /* KEYCMD command */
1164 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1165 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1166 if(tokens[0][0] == 0) {
1167 /* should incremental commands want each individual "keystroke"
1168 sent in a loop or the whole string in one go like now? */
1169 g_string_assign(uzbl.state.keycmd, tokens[1]);
1171 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1178 else if( (ctl_line[0] == '#')
1179 || (ctl_line[0] == ' ')
1180 || (ctl_line[0] == '\n'))
1181 ; /* ignore these lines */
1183 printf("Command not understood (%s)\n", ctl_line);
1189 build_stream_name(int type, const gchar* dir) {
1191 State *s = &uzbl.state;
1194 xwin_str = itos((int)uzbl.xwin);
1196 str = g_strdup_printf
1197 ("%s/uzbl_fifo_%s", dir,
1198 s->instance_name ? s->instance_name : xwin_str);
1199 } else if (type == SOCKET) {
1200 str = g_strdup_printf
1201 ("%s/uzbl_socket_%s", dir,
1202 s->instance_name ? s->instance_name : xwin_str );
1209 control_fifo(GIOChannel *gio, GIOCondition condition) {
1210 if (uzbl.state.verbose)
1211 printf("triggered\n");
1216 if (condition & G_IO_HUP)
1217 g_error ("Fifo: Read end of pipe died!\n");
1220 g_error ("Fifo: GIOChannel broke\n");
1222 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1223 if (ret == G_IO_STATUS_ERROR) {
1224 g_error ("Fifo: Error reading: %s\n", err->message);
1228 parse_cmd_line(ctl_line);
1235 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1236 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1237 if (unlink(uzbl.comm.fifo_path) == -1)
1238 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1239 g_free(uzbl.comm.fifo_path);
1240 uzbl.comm.fifo_path = NULL;
1243 if (*dir == ' ') { /* space unsets the variable */
1247 GIOChannel *chan = NULL;
1248 GError *error = NULL;
1249 gchar *path = build_stream_name(FIFO, dir);
1251 if (!file_exists(path)) {
1252 if (mkfifo (path, 0666) == 0) {
1253 // 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.
1254 chan = g_io_channel_new_file(path, "r+", &error);
1256 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1257 if (uzbl.state.verbose)
1258 printf ("init_fifo: created successfully as %s\n", path);
1259 uzbl.comm.fifo_path = path;
1261 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1262 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1263 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1264 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1266 /* if we got this far, there was an error; cleanup */
1267 if (error) g_error_free (error);
1273 control_stdin(GIOChannel *gio, GIOCondition condition) {
1275 gchar *ctl_line = NULL;
1278 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1279 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1282 parse_cmd_line(ctl_line);
1290 GIOChannel *chan = NULL;
1291 GError *error = NULL;
1293 chan = g_io_channel_unix_new(fileno(stdin));
1295 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1296 g_error ("Stdin: could not add watch\n");
1298 if (uzbl.state.verbose)
1299 printf ("Stdin: watch added successfully\n");
1302 g_error ("Stdin: Error while opening: %s\n", error->message);
1304 if (error) g_error_free (error);
1308 control_socket(GIOChannel *chan) {
1309 struct sockaddr_un remote;
1310 char buffer[512], *ctl_line;
1312 int sock, clientsock, n, done;
1315 sock = g_io_channel_unix_get_fd(chan);
1317 memset (buffer, 0, sizeof (buffer));
1319 t = sizeof (remote);
1320 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1324 memset (temp, 0, sizeof (temp));
1325 n = recv (clientsock, temp, 128, 0);
1327 buffer[strlen (buffer)] = '\0';
1331 strcat (buffer, temp);
1334 if (strcmp (buffer, "\n") < 0) {
1335 buffer[strlen (buffer) - 1] = '\0';
1337 buffer[strlen (buffer)] = '\0';
1340 ctl_line = g_strdup(buffer);
1341 parse_cmd_line (ctl_line);
1344 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1345 GError *error = NULL;
1348 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1349 if (ret == G_IO_STATUS_ERROR)
1350 g_error ("Error reading: %s\n", error->message);
1352 printf("Got line %s (%u bytes) \n",ctl_line, len);
1354 parse_line(ctl_line);
1362 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1363 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1364 if (unlink(uzbl.comm.socket_path) == -1)
1365 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1366 g_free(uzbl.comm.socket_path);
1367 uzbl.comm.socket_path = NULL;
1375 GIOChannel *chan = NULL;
1377 struct sockaddr_un local;
1378 gchar *path = build_stream_name(SOCKET, dir);
1380 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1382 local.sun_family = AF_UNIX;
1383 strcpy (local.sun_path, path);
1384 unlink (local.sun_path);
1386 len = strlen (local.sun_path) + sizeof (local.sun_family);
1387 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1388 if (uzbl.state.verbose)
1389 printf ("init_socket: opened in %s\n", path);
1392 if( (chan = g_io_channel_unix_new(sock)) ) {
1393 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1394 uzbl.comm.socket_path = path;
1397 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1399 /* if we got this far, there was an error; cleanup */
1406 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1407 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1409 // this function may be called very early when the templates are not set (yet), hence the checks
1411 update_title (void) {
1412 Behaviour *b = &uzbl.behave;
1415 if (b->show_status) {
1416 if (b->title_format_short) {
1417 parsed = expand_template(b->title_format_short);
1418 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1421 if (b->status_format) {
1422 parsed = expand_template(b->status_format);
1423 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1426 if (b->status_background) {
1428 gdk_color_parse (b->status_background, &color);
1429 //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)
1430 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1433 if (b->title_format_long) {
1434 parsed = expand_template(b->title_format_long);
1435 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1442 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1444 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1448 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1449 || 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)
1452 /* turn off insert mode (if always_insert_mode is not used) */
1453 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1454 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1459 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1462 if (event->keyval == GDK_Escape) {
1463 g_string_truncate(uzbl.state.keycmd, 0);
1468 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1469 if (event->keyval == GDK_Insert) {
1471 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1472 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1474 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1477 g_string_append (uzbl.state.keycmd, str);
1484 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1485 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1489 gboolean key_ret = FALSE;
1490 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1492 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1494 run_keycmd(key_ret);
1496 if (key_ret) return (!uzbl.behave.insert_mode);
1501 run_keycmd(const gboolean key_ret) {
1502 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1504 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1505 g_string_truncate(uzbl.state.keycmd, 0);
1506 parse_command(action->name, action->param);
1510 /* try if it's an incremental keycmd or one that takes args, and run it */
1511 GString* short_keys = g_string_new ("");
1512 GString* short_keys_inc = g_string_new ("");
1514 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1515 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1516 g_string_assign(short_keys_inc, short_keys->str);
1517 g_string_append_c(short_keys, '_');
1518 g_string_append_c(short_keys_inc, '*');
1520 gboolean exec_now = FALSE;
1521 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1522 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1523 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1524 if (key_ret) { /* just quit the incremental command on return */
1525 g_string_truncate(uzbl.state.keycmd, 0);
1527 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1531 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1532 GString* actionname = g_string_new ("");
1533 GString* actionparam = g_string_new ("");
1534 g_string_erase (parampart, 0, i+1);
1536 g_string_printf (actionname, action->name, parampart->str);
1538 g_string_printf (actionparam, action->param, parampart->str);
1539 parse_command(actionname->str, actionparam->str);
1540 g_string_free (actionname, TRUE);
1541 g_string_free (actionparam, TRUE);
1542 g_string_free (parampart, TRUE);
1544 g_string_truncate(uzbl.state.keycmd, 0);
1548 g_string_truncate(short_keys, short_keys->len - 1);
1550 g_string_free (short_keys, TRUE);
1551 g_string_free (short_keys_inc, TRUE);
1558 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1559 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
1561 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1562 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1564 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1565 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1566 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1567 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1568 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1569 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1570 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1571 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1572 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1573 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1574 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1576 return scrolled_window;
1583 g->mainbar = gtk_hbox_new (FALSE, 0);
1585 g->mainbar_label = gtk_label_new ("");
1586 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1587 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1588 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1589 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1590 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1595 GtkWidget* create_window () {
1596 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1597 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1598 gtk_widget_set_name (window, "Uzbl browser");
1599 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1605 run_handler (const gchar *act, const gchar *args) {
1606 char **parts = g_strsplit(act, " ", 2);
1608 else if ((g_strcmp0(parts[0], "spawn") == 0)
1609 || (g_strcmp0(parts[0], "sh") == 0)) {
1611 GString *a = g_string_new ("");
1613 spawnparts = split_quoted(parts[1], FALSE);
1614 g_string_append_printf(a, "%s", spawnparts[0]);
1615 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1616 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1617 g_string_append_printf(a, " %s", spawnparts[i]);
1618 parse_command(parts[0], a->str);
1619 g_string_free (a, TRUE);
1620 g_strfreev (spawnparts);
1622 parse_command(parts[0], parts[1]);
1627 add_binding (const gchar *key, const gchar *act) {
1628 char **parts = g_strsplit(act, " ", 2);
1635 if (uzbl.state.verbose)
1636 printf ("Binding %-10s : %s\n", key, act);
1638 action = new_action(parts[0], parts[1]);
1639 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1645 get_xdg_var (XDG_Var xdg) {
1646 const gchar* actual_value = getenv (xdg.environmental);
1647 const gchar* home = getenv ("HOME");
1649 gchar* return_value = str_replace ("~", home, actual_value);
1651 if (! actual_value || strcmp (actual_value, "") == 0) {
1652 if (xdg.default_value) {
1653 return_value = str_replace ("~", home, xdg.default_value);
1655 return_value = NULL;
1658 return return_value;
1662 find_xdg_file (int xdg_type, char* filename) {
1663 /* xdg_type = 0 => config
1664 xdg_type = 1 => data
1665 xdg_type = 2 => cache*/
1667 gchar* temporary_file = malloc (1024);
1668 gchar* temporary_string = NULL;
1672 buf = get_xdg_var (XDG[xdg_type]);
1673 strcpy (temporary_file, buf);
1674 strcat (temporary_file, filename);
1677 if (! file_exists (temporary_file) && xdg_type != 2) {
1678 buf = get_xdg_var (XDG[3 + xdg_type]);
1679 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1682 while (temporary_string && ! file_exists (temporary_file)) {
1683 strcpy (temporary_file, temporary_string);
1684 strcat (temporary_file, filename);
1685 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1689 if (file_exists (temporary_file)) {
1690 return temporary_file;
1698 State *s = &uzbl.state;
1699 Network *n = &uzbl.net;
1701 uzbl.behave.reset_command_mode = 1;
1703 if (!s->config_file) {
1704 s->config_file = find_xdg_file (0, "/uzbl/config");
1707 if (s->config_file) {
1708 GIOChannel *chan = NULL;
1709 gchar *readbuf = NULL;
1712 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1715 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1716 == G_IO_STATUS_NORMAL) {
1717 parse_cmd_line(readbuf);
1721 g_io_channel_unref (chan);
1722 if (uzbl.state.verbose)
1723 printf ("Config %s loaded\n", s->config_file);
1725 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1728 if (uzbl.state.verbose)
1729 printf ("No configuration file loaded.\n");
1731 if (!uzbl.behave.status_format)
1732 set_var_value("status_format", STATUS_DEFAULT);
1733 if (!uzbl.behave.title_format_long)
1734 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1735 if (!uzbl.behave.title_format_short)
1736 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1739 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1743 set_useragent(gchar *val) {
1748 gchar *ua = expand_template(val);
1750 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1754 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1757 if (!uzbl.behave.cookie_handler) return;
1759 gchar * stdout = NULL;
1760 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1761 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1762 gchar *action = g_strdup ("GET");
1763 SoupURI * soup_uri = soup_message_get_uri(msg);
1764 sharg_append(a, action);
1765 sharg_append(a, soup_uri->host);
1766 sharg_append(a, soup_uri->path);
1767 run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1768 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1770 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1773 g_array_free(a, TRUE);
1777 save_cookies (SoupMessage *msg, gpointer user_data){
1781 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1782 cookie = soup_cookie_to_set_cookie_header(ck->data);
1783 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1784 SoupURI * soup_uri = soup_message_get_uri(msg);
1785 gchar *action = strdup("PUT");
1786 sharg_append(a, action);
1787 sharg_append(a, soup_uri->host);
1788 sharg_append(a, soup_uri->path);
1789 sharg_append(a, cookie);
1790 run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1793 g_array_free(a, TRUE);
1800 main (int argc, char* argv[]) {
1801 gtk_init (&argc, &argv);
1802 if (!g_thread_supported ())
1803 g_thread_init (NULL);
1805 uzbl.state.executable_path = g_strdup(argv[0]);
1806 uzbl.state.selected_url = NULL;
1807 uzbl.state.searchtx = NULL;
1809 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1810 g_option_context_add_main_entries (context, entries, NULL);
1811 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1812 g_option_context_parse (context, &argc, &argv, NULL);
1813 g_option_context_free(context);
1814 /* initialize hash table */
1815 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1817 uzbl.net.soup_session = webkit_get_default_session();
1818 uzbl.state.keycmd = g_string_new("");
1820 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1821 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1822 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1823 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1825 if(uname(&uzbl.state.unameinfo) == -1)
1826 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1831 make_var_to_name_hash();
1834 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1836 uzbl.gui.scrolled_win = create_browser();
1839 /* initial packing */
1840 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1841 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1843 uzbl.gui.main_window = create_window ();
1844 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1847 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1848 gtk_widget_show_all (uzbl.gui.main_window);
1849 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1851 if (uzbl.state.verbose) {
1852 printf("Uzbl start location: %s\n", argv[0]);
1853 printf("window_id %i\n",(int) uzbl.xwin);
1854 printf("pid %i\n", getpid ());
1855 printf("name: %s\n", uzbl.state.instance_name);
1858 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1859 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1860 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1861 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1862 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1866 if (!uzbl.behave.show_status)
1867 gtk_widget_hide(uzbl.gui.mainbar);
1873 if(uzbl.state.uri) {
1874 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1875 g_array_append_val(a, uzbl.state.uri);
1876 load_uri (uzbl.gui.web_view, a);
1877 g_array_free (a, TRUE);
1883 return EXIT_SUCCESS;
1886 /* vi: set et ts=4: */