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>
61 /* define names and pointers to all config specific variables */
62 typedef const struct {
68 enum {TYPE_INT, TYPE_STRING};
73 } var_name_to_ptr[] = {
74 /* variable name pointer to variable in code variable type callback function */
75 /* ------------------------------------------------------------------------------------------------------------------- */
76 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
77 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
78 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
79 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
80 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
81 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
82 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
83 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
84 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
85 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
86 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
87 { "modkey" , {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
88 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
89 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
90 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
91 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
92 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
93 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
94 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
95 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
96 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
97 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
98 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
99 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
100 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
101 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
102 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
103 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
104 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
105 }, *n2v_p = var_name_to_ptr;
111 { "SHIFT", GDK_SHIFT_MASK }, // shift
112 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
113 { "CONTROL", GDK_CONTROL_MASK }, // control
114 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
115 { "MOD2", GDK_MOD2_MASK }, // 5th mod
116 { "MOD3", GDK_MOD3_MASK }, // 6th mod
117 { "MOD4", GDK_MOD4_MASK }, // 7th mod
118 { "MOD5", GDK_MOD5_MASK }, // 8th mod
119 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
120 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
121 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
122 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
123 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
124 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
125 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
126 { "META", GDK_META_MASK }, // meta (since 2.10)
131 /* construct a hash from the var_name_to_ptr array for quick access */
133 make_var_to_name_hash() {
134 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
136 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
141 /* commandline arguments (set initial values for the state variables) */
142 static GOptionEntry entries[] =
144 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
145 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
146 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
147 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
148 { NULL, 0, 0, 0, NULL, NULL, NULL }
151 typedef void (*Command)(WebKitWebView*, GArray *argv);
153 /* --- UTILITY FUNCTIONS --- */
159 snprintf(tmp, sizeof(tmp), "%i", val);
160 return g_strdup(tmp);
164 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
167 str_replace (const char* search, const char* replace, const char* string) {
171 buf = g_strsplit (string, search, -1);
172 ret = g_strjoinv (replace, buf);
179 setup_signal(int signr, sigfunc *shandler) {
180 struct sigaction nh, oh;
182 nh.sa_handler = shandler;
183 sigemptyset(&nh.sa_mask);
186 if(sigaction(signr, &nh, &oh) < 0)
194 if (uzbl.behave.fifo_dir)
195 unlink (uzbl.comm.fifo_path);
196 if (uzbl.behave.socket_dir)
197 unlink (uzbl.comm.socket_path);
199 g_free(uzbl.state.executable_path);
200 g_string_free(uzbl.state.keycmd, TRUE);
201 g_hash_table_destroy(uzbl.bindings);
202 g_hash_table_destroy(uzbl.behave.commands);
206 /* --- SIGNAL HANDLER --- */
209 catch_sigterm(int s) {
215 catch_sigint(int s) {
221 /* --- CALLBACKS --- */
224 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
227 (void) navigation_action;
228 (void) policy_decision;
230 const gchar* uri = webkit_network_request_get_uri (request);
231 if (uzbl.state.verbose)
232 printf("New window requested -> %s \n", uri);
233 new_window_load_uri(uri);
238 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
242 if (uzbl.state.selected_url != NULL) {
243 if (uzbl.state.verbose)
244 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
245 new_window_load_uri(uzbl.state.selected_url);
247 if (uzbl.state.verbose)
248 printf("New web view -> %s\n","Nothing to open, exiting");
254 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
257 if (uzbl.behave.download_handler) {
258 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
259 if (uzbl.state.verbose)
260 printf("Download -> %s\n",uri);
261 /* if urls not escaped, we may have to escape and quote uri before this call */
262 run_handler(uzbl.behave.download_handler, uri);
267 /* scroll a bar in a given direction */
269 scroll (GtkAdjustment* bar, GArray *argv) {
273 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
274 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
275 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
278 static void scroll_begin(WebKitWebView* page, GArray *argv) {
279 (void) page; (void) argv;
280 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
283 static void scroll_end(WebKitWebView* page, GArray *argv) {
284 (void) page; (void) argv;
285 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
286 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
289 static void scroll_vert(WebKitWebView* page, GArray *argv) {
291 scroll(uzbl.gui.bar_v, argv);
294 static void scroll_horz(WebKitWebView* page, GArray *argv) {
296 scroll(uzbl.gui.bar_h, argv);
301 if (!uzbl.behave.show_status) {
302 gtk_widget_hide(uzbl.gui.mainbar);
304 gtk_widget_show(uzbl.gui.mainbar);
310 toggle_status_cb (WebKitWebView* page, GArray *argv) {
314 if (uzbl.behave.show_status) {
315 gtk_widget_hide(uzbl.gui.mainbar);
317 gtk_widget_show(uzbl.gui.mainbar);
319 uzbl.behave.show_status = !uzbl.behave.show_status;
324 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
328 //Set selected_url state variable
329 g_free(uzbl.state.selected_url);
330 uzbl.state.selected_url = NULL;
332 uzbl.state.selected_url = g_strdup(link);
338 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
342 if (uzbl.gui.main_title)
343 g_free (uzbl.gui.main_title);
344 uzbl.gui.main_title = g_strdup (title);
349 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
352 uzbl.gui.sbar.load_progress = progress;
357 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
361 if (uzbl.behave.load_finish_handler)
362 run_handler(uzbl.behave.load_finish_handler, "");
366 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
370 if (uzbl.behave.load_start_handler)
371 run_handler(uzbl.behave.load_start_handler, "");
375 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
378 free (uzbl.state.uri);
379 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
380 uzbl.state.uri = g_string_free (newuri, FALSE);
381 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
382 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
385 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
386 if (uzbl.behave.load_commit_handler)
387 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
391 destroy_cb (GtkWidget* widget, gpointer data) {
399 if (uzbl.behave.history_handler) {
401 struct tm * timeinfo;
404 timeinfo = localtime ( &rawtime );
405 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
406 run_handler(uzbl.behave.history_handler, date);
411 /* VIEW funcs (little webkit wrappers) */
412 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
414 VIEWFUNC(reload_bypass_cache)
415 VIEWFUNC(stop_loading)
422 /* -- command to callback/function map for things we cannot attach to any signals */
425 static struct {char *name; Command command[2];} cmdlist[] =
426 { /* key function no_split */
427 { "back", {view_go_back, 0} },
428 { "forward", {view_go_forward, 0} },
429 { "scroll_vert", {scroll_vert, 0} },
430 { "scroll_horz", {scroll_horz, 0} },
431 { "scroll_begin", {scroll_begin, 0} },
432 { "scroll_end", {scroll_end, 0} },
433 { "reload", {view_reload, 0}, },
434 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
435 { "stop", {view_stop_loading, 0}, },
436 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
437 { "zoom_out", {view_zoom_out, 0}, },
438 { "uri", {load_uri, NOSPLIT} },
439 { "script", {run_js, NOSPLIT} },
440 { "toggle_status", {toggle_status_cb, 0} },
441 { "spawn", {spawn, 0} },
442 { "sh", {spawn_sh, 0} },
443 { "exit", {close_uzbl, 0} },
444 { "search", {search_forward_text, NOSPLIT} },
445 { "search_reverse", {search_reverse_text, NOSPLIT} },
446 { "insert_mode", {set_insert_mode, 0} },
447 { "runcmd", {runcmd, NOSPLIT} }
454 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
456 for (i = 0; i < LENGTH(cmdlist); i++)
457 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
460 /* -- CORE FUNCTIONS -- */
463 free_action(gpointer act) {
464 Action *action = (Action*)act;
465 g_free(action->name);
467 g_free(action->param);
472 new_action(const gchar *name, const gchar *param) {
473 Action *action = g_new(Action, 1);
475 action->name = g_strdup(name);
477 action->param = g_strdup(param);
479 action->param = NULL;
485 file_exists (const char * filename) {
486 return (access(filename, F_OK) == 0);
490 set_insert_mode(WebKitWebView *page, GArray *argv) {
494 uzbl.behave.insert_mode = TRUE;
499 load_uri (WebKitWebView *web_view, GArray *argv) {
500 if (argv_idx(argv, 0)) {
501 GString* newuri = g_string_new (argv_idx(argv, 0));
502 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
503 g_string_prepend (newuri, "http://");
504 /* if we do handle cookies, ask our handler for them */
505 webkit_web_view_load_uri (web_view, newuri->str);
506 g_string_free (newuri, TRUE);
511 run_js (WebKitWebView * web_view, GArray *argv) {
512 if (argv_idx(argv, 0))
513 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
517 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
518 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
519 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
521 if (uzbl.state.searchtx != NULL) {
522 if (uzbl.state.verbose)
523 printf ("Searching: %s\n", uzbl.state.searchtx);
524 webkit_web_view_unmark_text_matches (page);
525 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
526 webkit_web_view_set_highlight_text_matches (page, TRUE);
527 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
528 g_free(uzbl.state.searchtx);
529 uzbl.state.searchtx = NULL;
534 search_forward_text (WebKitWebView *page, GArray *argv) {
535 search_text(page, argv, TRUE);
539 search_reverse_text (WebKitWebView *page, GArray *argv) {
540 search_text(page, argv, FALSE);
544 new_window_load_uri (const gchar * uri) {
545 GString* to_execute = g_string_new ("");
546 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
548 for (i = 0; entries[i].long_name != NULL; i++) {
549 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
550 gchar** str = (gchar**)entries[i].arg_data;
552 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
556 if (uzbl.state.verbose)
557 printf("\n%s\n", to_execute->str);
558 g_spawn_command_line_async (to_execute->str, NULL);
559 g_string_free (to_execute, TRUE);
563 close_uzbl (WebKitWebView *page, GArray *argv) {
569 /* --Statusbar functions-- */
571 build_progressbar_ascii(int percent) {
575 GString *bar = g_string_new("");
577 l = (double)percent*((double)width/100.);
578 l = (int)(l+.5)>=(int)l ? l+.5 : l;
580 for(i=0; i<(int)l; i++)
581 g_string_append(bar, "=");
584 g_string_append(bar, "·");
586 return g_string_free(bar, FALSE);
591 const GScannerConfig scan_config = {
594 ) /* cset_skip_characters */,
599 ) /* cset_identifier_first */,
606 ) /* cset_identifier_nth */,
607 ( "" ) /* cpair_comment_single */,
609 TRUE /* case_sensitive */,
611 FALSE /* skip_comment_multi */,
612 FALSE /* skip_comment_single */,
613 FALSE /* scan_comment_multi */,
614 TRUE /* scan_identifier */,
615 TRUE /* scan_identifier_1char */,
616 FALSE /* scan_identifier_NULL */,
617 TRUE /* scan_symbols */,
618 FALSE /* scan_binary */,
619 FALSE /* scan_octal */,
620 FALSE /* scan_float */,
621 FALSE /* scan_hex */,
622 FALSE /* scan_hex_dollar */,
623 FALSE /* scan_string_sq */,
624 FALSE /* scan_string_dq */,
625 TRUE /* numbers_2_int */,
626 FALSE /* int_2_float */,
627 FALSE /* identifier_2_string */,
628 FALSE /* char_2_token */,
629 FALSE /* symbol_2_token */,
630 TRUE /* scope_0_fallback */,
635 uzbl.scan = g_scanner_new(&scan_config);
636 while(symp->symbol_name) {
637 g_scanner_scope_add_symbol(uzbl.scan, 0,
639 GINT_TO_POINTER(symp->symbol_token));
645 expand_template(const char *template) {
646 if(!template) return NULL;
648 GTokenType token = G_TOKEN_NONE;
649 GString *ret = g_string_new("");
653 g_scanner_input_text(uzbl.scan, template, strlen(template));
654 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
655 token = g_scanner_get_next_token(uzbl.scan);
657 if(token == G_TOKEN_SYMBOL) {
658 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
661 buf = uzbl.state.uri?
662 g_markup_printf_escaped("%s", uzbl.state.uri) :
664 g_string_append(ret, buf);
668 buf = itos(uzbl.gui.sbar.load_progress);
669 g_string_append(ret, buf);
672 case SYM_LOADPRGSBAR:
673 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
674 g_string_append(ret, buf);
678 buf = uzbl.gui.main_title?
679 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
681 g_string_append(ret, buf);
684 case SYM_SELECTED_URI:
685 buf = uzbl.state.selected_url?
686 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
688 g_string_append(ret, buf);
692 buf = itos(uzbl.xwin);
694 uzbl.state.instance_name?uzbl.state.instance_name:buf);
698 buf = uzbl.state.keycmd->str?
699 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
701 g_string_append(ret, buf);
706 uzbl.behave.insert_mode?"[I]":"[C]");
710 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
714 buf = itos(WEBKIT_MAJOR_VERSION);
715 g_string_append(ret, buf);
719 buf = itos(WEBKIT_MINOR_VERSION);
720 g_string_append(ret, buf);
724 buf = itos(WEBKIT_MICRO_VERSION);
725 g_string_append(ret, buf);
729 g_string_append(ret, uzbl.state.unameinfo.sysname);
732 g_string_append(ret, uzbl.state.unameinfo.nodename);
735 g_string_append(ret, uzbl.state.unameinfo.release);
738 g_string_append(ret, uzbl.state.unameinfo.version);
741 g_string_append(ret, uzbl.state.unameinfo.machine);
744 g_string_append(ret, ARCH);
748 g_string_append(ret, uzbl.state.unameinfo.domainname);
752 g_string_append(ret, COMMIT);
758 else if(token == G_TOKEN_INT) {
759 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
760 g_string_append(ret, buf);
763 else if(token == G_TOKEN_IDENTIFIER) {
764 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
766 else if(token == G_TOKEN_CHAR) {
767 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
771 return g_string_free(ret, FALSE);
773 /* --End Statusbar functions-- */
776 sharg_append(GArray *a, const gchar *str) {
777 const gchar *s = (str ? str : "");
778 g_array_append_val(a, s);
781 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
783 run_command (const gchar *command, const guint npre, const gchar **args,
784 const gboolean sync, char **stdout) {
785 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
788 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
789 gchar *pid = itos(getpid());
790 gchar *xwin = itos(uzbl.xwin);
792 sharg_append(a, command);
793 for (i = 0; i < npre; i++) /* add n args before the default vars */
794 sharg_append(a, args[i]);
795 sharg_append(a, uzbl.state.config_file);
796 sharg_append(a, pid);
797 sharg_append(a, xwin);
798 sharg_append(a, uzbl.comm.fifo_path);
799 sharg_append(a, uzbl.comm.socket_path);
800 sharg_append(a, uzbl.state.uri);
801 sharg_append(a, uzbl.gui.main_title);
803 for (i = npre; i < g_strv_length((gchar**)args); i++)
804 sharg_append(a, args[i]);
806 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
807 NULL, NULL, stdout, NULL, NULL, &err);
808 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
809 NULL, NULL, NULL, &err);
811 if (uzbl.state.verbose) {
812 GString *s = g_string_new("spawned:");
813 for (i = 0; i < (a->len); i++) {
814 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
815 g_string_append_printf(s, " %s", qarg);
818 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
819 printf("%s\n", s->str);
820 g_string_free(s, TRUE);
823 g_printerr("error on run_command: %s\n", err->message);
828 g_array_free (a, TRUE);
833 split_quoted(const gchar* src, const gboolean unquote) {
834 /* split on unquoted space, return array of strings;
835 remove a layer of quotes and backslashes if unquote */
836 if (!src) return NULL;
840 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
841 GString *s = g_string_new ("");
845 for (p = src; *p != '\0'; p++) {
846 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
847 else if (*p == '\\') { g_string_append_c(s, *p++);
848 g_string_append_c(s, *p); }
849 else if ((*p == '"') && unquote && !sq) dq = !dq;
850 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
852 else if ((*p == '\'') && unquote && !dq) sq = !sq;
853 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
855 else if ((*p == ' ') && !dq && !sq) {
856 dup = g_strdup(s->str);
857 g_array_append_val(a, dup);
858 g_string_truncate(s, 0);
859 } else g_string_append_c(s, *p);
861 dup = g_strdup(s->str);
862 g_array_append_val(a, dup);
863 ret = (gchar**)a->data;
864 g_array_free (a, FALSE);
865 g_string_free (s, FALSE);
870 spawn(WebKitWebView *web_view, GArray *argv) {
872 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
873 if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, argv->data + sizeof(gchar*), FALSE, NULL);
877 spawn_sh(WebKitWebView *web_view, GArray *argv) {
879 if (!uzbl.behave.shell_cmd) {
880 g_printerr ("spawn_sh: shell_cmd is not set!\n");
885 gchar *spacer = g_strdup("");
886 g_array_insert_val(argv, 1, spacer);
887 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
889 for (i = 1; i < g_strv_length(cmd); i++)
890 g_array_prepend_val(argv, cmd[i]);
892 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, argv->data, FALSE, NULL);
898 parse_command(const char *cmd, const char *param) {
901 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
904 gchar **par = split_quoted(param, TRUE);
905 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
907 if (c[1]) { /* don't split */
908 sharg_append(a, param);
910 for (i = 0; i < g_strv_length(par); i++)
911 sharg_append(a, par[i]);
913 c[0](uzbl.gui.web_view, a);
915 g_array_free (a, TRUE);
918 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
924 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
925 G_REGEX_OPTIMIZE, 0, NULL);
926 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
927 G_REGEX_OPTIMIZE, 0, NULL);
928 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
929 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
930 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
931 G_REGEX_OPTIMIZE, 0, NULL);
932 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
933 G_REGEX_OPTIMIZE, 0, NULL);
937 get_var_value(gchar *name) {
940 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
941 if(c->type == TYPE_STRING)
942 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
943 else if(c->type == TYPE_INT)
944 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
953 if(*uzbl.net.proxy_url == ' '
954 || uzbl.net.proxy_url == NULL) {
955 soup_session_remove_feature_by_type(uzbl.net.soup_session,
956 (GType) SOUP_SESSION_PROXY_URI);
959 suri = soup_uri_new(uzbl.net.proxy_url);
960 g_object_set(G_OBJECT(uzbl.net.soup_session),
961 SOUP_SESSION_PROXY_URI,
970 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
971 g_array_append_val (a, uzbl.state.uri);
972 load_uri(uzbl.gui.web_view, a);
973 g_array_free (a, TRUE);
977 cmd_always_insert_mode() {
978 uzbl.behave.insert_mode =
979 uzbl.behave.always_insert_mode ? TRUE : FALSE;
985 g_object_set(G_OBJECT(uzbl.net.soup_session),
986 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
990 cmd_max_conns_host() {
991 g_object_set(G_OBJECT(uzbl.net.soup_session),
992 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
997 soup_session_remove_feature
998 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
999 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1000 /*g_free(uzbl.net.soup_logger);*/
1002 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1003 soup_session_add_feature(uzbl.net.soup_session,
1004 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1008 cmd_default_font_size() {
1009 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1010 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1014 cmd_minimum_font_size() {
1015 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1016 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1023 buf = init_fifo(uzbl.behave.fifo_dir);
1024 if(uzbl.behave.fifo_dir)
1025 free(uzbl.behave.fifo_dir);
1027 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1034 buf = init_socket(uzbl.behave.fifo_dir);
1035 if(uzbl.behave.socket_dir)
1036 free(uzbl.behave.socket_dir);
1038 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1046 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1047 uzbl.behave.modmask = 0;
1049 if(uzbl.behave.modkey)
1050 free(uzbl.behave.modkey);
1052 for (i = 0; modkeys[i].key != NULL; i++) {
1053 if (g_strrstr(uzbl.behave.modkey, modkeys[i].key))
1054 uzbl.behave.modmask |= modkeys[i].mask;
1062 buf = set_useragent(uzbl.net.useragent);
1063 if(uzbl.net.useragent)
1064 free(uzbl.net.useragent);
1066 uzbl.net.useragent = buf?buf:g_strdup("");
1071 gtk_widget_ref(uzbl.gui.scrolled_win);
1072 gtk_widget_ref(uzbl.gui.mainbar);
1073 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1074 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1076 if(uzbl.behave.status_top) {
1077 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1078 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1081 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1082 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1084 gtk_widget_unref(uzbl.gui.scrolled_win);
1085 gtk_widget_unref(uzbl.gui.mainbar);
1086 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1091 set_var_value(gchar *name, gchar *val) {
1093 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) {
1104 *ip = (int)strtoul(val, &endp, 10);
1107 /* invoke a command specific function */
1108 if(c->func) c->func();
1110 /* this will be removed as soon as we have converted to
1111 * the callback interface
1119 runcmd(WebKitWebView* page, GArray *argv) {
1121 parse_cmd_line(argv_idx(argv, 0));
1125 parse_cmd_line(const char *ctl_line) {
1129 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1130 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1131 if(tokens[0][0] == 0) {
1132 set_var_value(tokens[1], tokens[2]);
1136 printf("Error in command: %s\n", tokens[0]);
1139 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1140 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1141 if(tokens[0][0] == 0) {
1142 get_var_value(tokens[1]);
1146 printf("Error in command: %s\n", tokens[0]);
1149 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1150 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1151 if(tokens[0][0] == 0) {
1152 add_binding(tokens[1], tokens[2]);
1156 printf("Error in command: %s\n", tokens[0]);
1159 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1160 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1161 if(tokens[0][0] == 0) {
1162 parse_command(tokens[1], tokens[2]);
1166 printf("Error in command: %s\n", tokens[0]);
1168 /* KEYCMD command */
1169 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1170 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1171 if(tokens[0][0] == 0) {
1172 /* should incremental commands want each individual "keystroke"
1173 sent in a loop or the whole string in one go like now? */
1174 g_string_assign(uzbl.state.keycmd, tokens[1]);
1176 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1183 else if( (ctl_line[0] == '#')
1184 || (ctl_line[0] == ' ')
1185 || (ctl_line[0] == '\n'))
1186 ; /* ignore these lines */
1188 printf("Command not understood (%s)\n", ctl_line);
1194 build_stream_name(int type, const gchar* dir) {
1196 State *s = &uzbl.state;
1199 xwin_str = itos((int)uzbl.xwin);
1201 str = g_strdup_printf
1202 ("%s/uzbl_fifo_%s", dir,
1203 s->instance_name ? s->instance_name : xwin_str);
1204 } else if (type == SOCKET) {
1205 str = g_strdup_printf
1206 ("%s/uzbl_socket_%s", dir,
1207 s->instance_name ? s->instance_name : xwin_str );
1214 control_fifo(GIOChannel *gio, GIOCondition condition) {
1215 if (uzbl.state.verbose)
1216 printf("triggered\n");
1221 if (condition & G_IO_HUP)
1222 g_error ("Fifo: Read end of pipe died!\n");
1225 g_error ("Fifo: GIOChannel broke\n");
1227 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1228 if (ret == G_IO_STATUS_ERROR) {
1229 g_error ("Fifo: Error reading: %s\n", err->message);
1233 parse_cmd_line(ctl_line);
1240 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1241 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1242 if (unlink(uzbl.comm.fifo_path) == -1)
1243 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1244 g_free(uzbl.comm.fifo_path);
1245 uzbl.comm.fifo_path = NULL;
1248 if (*dir == ' ') { /* space unsets the variable */
1252 GIOChannel *chan = NULL;
1253 GError *error = NULL;
1254 gchar *path = build_stream_name(FIFO, dir);
1256 if (!file_exists(path)) {
1257 if (mkfifo (path, 0666) == 0) {
1258 // 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.
1259 chan = g_io_channel_new_file(path, "r+", &error);
1261 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1262 if (uzbl.state.verbose)
1263 printf ("init_fifo: created successfully as %s\n", path);
1264 uzbl.comm.fifo_path = path;
1266 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1267 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1268 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1269 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1271 /* if we got this far, there was an error; cleanup */
1272 if (error) g_error_free (error);
1278 control_stdin(GIOChannel *gio, GIOCondition condition) {
1280 gchar *ctl_line = NULL;
1283 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1284 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1287 parse_cmd_line(ctl_line);
1295 GIOChannel *chan = NULL;
1296 GError *error = NULL;
1298 chan = g_io_channel_unix_new(fileno(stdin));
1300 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1301 g_error ("Stdin: could not add watch\n");
1303 if (uzbl.state.verbose)
1304 printf ("Stdin: watch added successfully\n");
1307 g_error ("Stdin: Error while opening: %s\n", error->message);
1309 if (error) g_error_free (error);
1313 control_socket(GIOChannel *chan) {
1314 struct sockaddr_un remote;
1315 char buffer[512], *ctl_line;
1317 int sock, clientsock, n, done;
1320 sock = g_io_channel_unix_get_fd(chan);
1322 memset (buffer, 0, sizeof (buffer));
1324 t = sizeof (remote);
1325 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1329 memset (temp, 0, sizeof (temp));
1330 n = recv (clientsock, temp, 128, 0);
1332 buffer[strlen (buffer)] = '\0';
1336 strcat (buffer, temp);
1339 if (strcmp (buffer, "\n") < 0) {
1340 buffer[strlen (buffer) - 1] = '\0';
1342 buffer[strlen (buffer)] = '\0';
1345 ctl_line = g_strdup(buffer);
1346 parse_cmd_line (ctl_line);
1349 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1350 GError *error = NULL;
1353 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1354 if (ret == G_IO_STATUS_ERROR)
1355 g_error ("Error reading: %s\n", error->message);
1357 printf("Got line %s (%u bytes) \n",ctl_line, len);
1359 parse_line(ctl_line);
1367 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1368 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1369 if (unlink(uzbl.comm.socket_path) == -1)
1370 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1371 g_free(uzbl.comm.socket_path);
1372 uzbl.comm.socket_path = NULL;
1380 GIOChannel *chan = NULL;
1382 struct sockaddr_un local;
1383 gchar *path = build_stream_name(SOCKET, dir);
1385 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1387 local.sun_family = AF_UNIX;
1388 strcpy (local.sun_path, path);
1389 unlink (local.sun_path);
1391 len = strlen (local.sun_path) + sizeof (local.sun_family);
1392 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1393 if (uzbl.state.verbose)
1394 printf ("init_socket: opened in %s\n", path);
1397 if( (chan = g_io_channel_unix_new(sock)) ) {
1398 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1399 uzbl.comm.socket_path = path;
1402 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1404 /* if we got this far, there was an error; cleanup */
1411 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1412 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1414 // this function may be called very early when the templates are not set (yet), hence the checks
1416 update_title (void) {
1417 Behaviour *b = &uzbl.behave;
1420 if (b->show_status) {
1421 if (b->title_format_short) {
1422 parsed = expand_template(b->title_format_short);
1423 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1426 if (b->status_format) {
1427 parsed = expand_template(b->status_format);
1428 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1431 if (b->status_background) {
1433 gdk_color_parse (b->status_background, &color);
1434 //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)
1435 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1438 if (b->title_format_long) {
1439 parsed = expand_template(b->title_format_long);
1440 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1447 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1449 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1453 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1454 || 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)
1457 /* turn off insert mode (if always_insert_mode is not used) */
1458 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1459 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1464 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1467 if (event->keyval == GDK_Escape) {
1468 g_string_truncate(uzbl.state.keycmd, 0);
1473 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1474 if (event->keyval == GDK_Insert) {
1476 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1477 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1479 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1482 g_string_append (uzbl.state.keycmd, str);
1489 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1490 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1494 gboolean key_ret = FALSE;
1495 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1497 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1499 run_keycmd(key_ret);
1501 if (key_ret) return (!uzbl.behave.insert_mode);
1506 run_keycmd(const gboolean key_ret) {
1507 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1509 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1510 g_string_truncate(uzbl.state.keycmd, 0);
1511 parse_command(action->name, action->param);
1515 /* try if it's an incremental keycmd or one that takes args, and run it */
1516 GString* short_keys = g_string_new ("");
1517 GString* short_keys_inc = g_string_new ("");
1519 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1520 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1521 g_string_assign(short_keys_inc, short_keys->str);
1522 g_string_append_c(short_keys, '_');
1523 g_string_append_c(short_keys_inc, '*');
1525 gboolean exec_now = FALSE;
1526 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1527 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1528 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1529 if (key_ret) { /* just quit the incremental command on return */
1530 g_string_truncate(uzbl.state.keycmd, 0);
1532 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1536 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1537 GString* actionname = g_string_new ("");
1538 GString* actionparam = g_string_new ("");
1539 g_string_erase (parampart, 0, i+1);
1541 g_string_printf (actionname, action->name, parampart->str);
1543 g_string_printf (actionparam, action->param, parampart->str);
1544 parse_command(actionname->str, actionparam->str);
1545 g_string_free (actionname, TRUE);
1546 g_string_free (actionparam, TRUE);
1547 g_string_free (parampart, TRUE);
1549 g_string_truncate(uzbl.state.keycmd, 0);
1553 g_string_truncate(short_keys, short_keys->len - 1);
1555 g_string_free (short_keys, TRUE);
1556 g_string_free (short_keys_inc, TRUE);
1563 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1564 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
1566 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1567 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1569 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1570 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1571 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1572 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1573 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1574 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1575 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1576 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1577 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1578 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1579 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1581 return scrolled_window;
1588 g->mainbar = gtk_hbox_new (FALSE, 0);
1590 g->mainbar_label = gtk_label_new ("");
1591 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1592 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1593 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1594 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1595 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1600 GtkWidget* create_window () {
1601 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1602 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1603 gtk_widget_set_name (window, "Uzbl browser");
1604 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1610 run_handler (const gchar *act, const gchar *args) {
1611 char **parts = g_strsplit(act, " ", 2);
1613 else if ((g_strcmp0(parts[0], "spawn") == 0)
1614 || (g_strcmp0(parts[0], "sh") == 0)) {
1616 GString *a = g_string_new ("");
1618 spawnparts = split_quoted(parts[1], FALSE);
1619 g_string_append_printf(a, "%s", spawnparts[0]);
1620 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1621 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1622 g_string_append_printf(a, " %s", spawnparts[i]);
1623 parse_command(parts[0], a->str);
1624 g_string_free (a, TRUE);
1625 g_strfreev (spawnparts);
1627 parse_command(parts[0], parts[1]);
1632 add_binding (const gchar *key, const gchar *act) {
1633 char **parts = g_strsplit(act, " ", 2);
1640 if (uzbl.state.verbose)
1641 printf ("Binding %-10s : %s\n", key, act);
1643 action = new_action(parts[0], parts[1]);
1644 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1650 get_xdg_var (XDG_Var xdg) {
1651 const gchar* actual_value = getenv (xdg.environmental);
1652 const gchar* home = getenv ("HOME");
1654 gchar* return_value = str_replace ("~", home, actual_value);
1656 if (! actual_value || strcmp (actual_value, "") == 0) {
1657 if (xdg.default_value) {
1658 return_value = str_replace ("~", home, xdg.default_value);
1660 return_value = NULL;
1663 return return_value;
1667 find_xdg_file (int xdg_type, char* filename) {
1668 /* xdg_type = 0 => config
1669 xdg_type = 1 => data
1670 xdg_type = 2 => cache*/
1672 gchar* temporary_file = malloc (1024);
1673 gchar* temporary_string = NULL;
1677 buf = get_xdg_var (XDG[xdg_type]);
1678 strcpy (temporary_file, buf);
1679 strcat (temporary_file, filename);
1682 if (! file_exists (temporary_file) && xdg_type != 2) {
1683 buf = get_xdg_var (XDG[3 + xdg_type]);
1684 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1687 while (temporary_string && ! file_exists (temporary_file)) {
1688 strcpy (temporary_file, temporary_string);
1689 strcat (temporary_file, filename);
1690 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1694 if (file_exists (temporary_file)) {
1695 return temporary_file;
1703 State *s = &uzbl.state;
1704 Network *n = &uzbl.net;
1706 uzbl.behave.reset_command_mode = 1;
1708 if (!s->config_file) {
1709 s->config_file = find_xdg_file (0, "/uzbl/config");
1712 if (s->config_file) {
1713 GIOChannel *chan = NULL;
1714 gchar *readbuf = NULL;
1717 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1720 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1721 == G_IO_STATUS_NORMAL) {
1722 parse_cmd_line(readbuf);
1726 g_io_channel_unref (chan);
1727 if (uzbl.state.verbose)
1728 printf ("Config %s loaded\n", s->config_file);
1730 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1733 if (uzbl.state.verbose)
1734 printf ("No configuration file loaded.\n");
1736 if (!uzbl.behave.status_format)
1737 set_var_value("status_format", STATUS_DEFAULT);
1738 if (!uzbl.behave.title_format_long)
1739 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1740 if (!uzbl.behave.title_format_short)
1741 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1744 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1748 set_useragent(gchar *val) {
1753 gchar *ua = expand_template(val);
1755 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1759 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1762 if (!uzbl.behave.cookie_handler) return;
1764 gchar * stdout = NULL;
1765 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1766 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1767 gchar *action = g_strdup ("GET");
1768 SoupURI * soup_uri = soup_message_get_uri(msg);
1769 sharg_append(a, action);
1770 sharg_append(a, soup_uri->host);
1771 sharg_append(a, soup_uri->path);
1772 run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1773 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1775 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1778 g_array_free(a, TRUE);
1782 save_cookies (SoupMessage *msg, gpointer user_data){
1786 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1787 cookie = soup_cookie_to_set_cookie_header(ck->data);
1788 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1789 SoupURI * soup_uri = soup_message_get_uri(msg);
1790 gchar *action = strdup("PUT");
1791 sharg_append(a, action);
1792 sharg_append(a, soup_uri->host);
1793 sharg_append(a, soup_uri->path);
1794 sharg_append(a, cookie);
1795 run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1798 g_array_free(a, TRUE);
1805 main (int argc, char* argv[]) {
1806 gtk_init (&argc, &argv);
1807 if (!g_thread_supported ())
1808 g_thread_init (NULL);
1810 uzbl.state.executable_path = g_strdup(argv[0]);
1811 uzbl.state.selected_url = NULL;
1812 uzbl.state.searchtx = NULL;
1814 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1815 g_option_context_add_main_entries (context, entries, NULL);
1816 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1817 g_option_context_parse (context, &argc, &argv, NULL);
1818 g_option_context_free(context);
1819 /* initialize hash table */
1820 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1822 uzbl.net.soup_session = webkit_get_default_session();
1823 uzbl.state.keycmd = g_string_new("");
1825 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1826 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1827 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1828 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1830 if(uname(&uzbl.state.unameinfo) == -1)
1831 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1836 make_var_to_name_hash();
1839 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1841 uzbl.gui.scrolled_win = create_browser();
1844 /* initial packing */
1845 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1846 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1848 uzbl.gui.main_window = create_window ();
1849 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1852 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1853 gtk_widget_show_all (uzbl.gui.main_window);
1854 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1856 if (uzbl.state.verbose) {
1857 printf("Uzbl start location: %s\n", argv[0]);
1858 printf("window_id %i\n",(int) uzbl.xwin);
1859 printf("pid %i\n", getpid ());
1860 printf("name: %s\n", uzbl.state.instance_name);
1863 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1864 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1865 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1866 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1867 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1871 if (!uzbl.behave.show_status)
1872 gtk_widget_hide(uzbl.gui.mainbar);
1878 //if(uzbl.state.uri)
1879 // load_uri (uzbl.gui.web_view, uzbl.state.uri);
1884 return EXIT_SUCCESS;
1887 /* vi: set et ts=4: */