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 */
65 } var_name_to_ptr[] = {
66 { "uri", (void *)&uzbl.state.uri },
67 { "status_message", (void *)&uzbl.gui.sbar.msg },
68 { "show_status", (void *)&uzbl.behave.show_status },
69 { "status_top", (void *)&uzbl.behave.status_top },
70 { "status_format", (void *)&uzbl.behave.status_format },
71 { "status_background", (void *)&uzbl.behave.status_background },
72 { "title_format_long", (void *)&uzbl.behave.title_format_long },
73 { "title_format_short", (void *)&uzbl.behave.title_format_short },
74 { "insert_mode", (void *)&uzbl.behave.insert_mode },
75 { "always_insert_mode", (void *)&uzbl.behave.always_insert_mode },
76 { "reset_command_mode", (void *)&uzbl.behave.reset_command_mode },
77 { "modkey" , (void *)&uzbl.behave.modkey },
78 { "load_finish_handler",(void *)&uzbl.behave.load_finish_handler},
79 { "load_start_handler", (void *)&uzbl.behave.load_start_handler },
80 { "load_commit_handler",(void *)&uzbl.behave.load_commit_handler},
81 { "history_handler", (void *)&uzbl.behave.history_handler },
82 { "download_handler", (void *)&uzbl.behave.download_handler },
83 { "cookie_handler", (void *)&uzbl.behave.cookie_handler },
84 { "fifo_dir", (void *)&uzbl.behave.fifo_dir },
85 { "socket_dir", (void *)&uzbl.behave.socket_dir },
86 { "http_debug", (void *)&uzbl.behave.http_debug },
87 { "default_font_size", (void *)&uzbl.behave.default_font_size },
88 { "minimum_font_size", (void *)&uzbl.behave.minimum_font_size },
89 { "shell_cmd", (void *)&uzbl.behave.shell_cmd },
90 { "proxy_url", (void *)&uzbl.net.proxy_url },
91 { "max_conns", (void *)&uzbl.net.max_conns },
92 { "max_conns_host", (void *)&uzbl.net.max_conns_host },
93 { "useragent", (void *)&uzbl.net.useragent },
95 }, *n2v_p = var_name_to_ptr;
101 { "SHIFT", GDK_SHIFT_MASK }, // shift
102 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
103 { "CONTROL", GDK_CONTROL_MASK }, // control
104 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
105 { "MOD2", GDK_MOD2_MASK }, // 5th mod
106 { "MOD3", GDK_MOD3_MASK }, // 6th mod
107 { "MOD4", GDK_MOD4_MASK }, // 7th mod
108 { "MOD5", GDK_MOD5_MASK }, // 8th mod
109 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
110 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
111 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
112 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
113 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
114 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
115 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
116 { "META", GDK_META_MASK }, // meta (since 2.10)
120 /* construct a hash from the var_name_to_ptr array for quick access */
122 make_var_to_name_hash() {
123 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
125 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
130 /* commandline arguments (set initial values for the state variables) */
131 static GOptionEntry entries[] =
133 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
134 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
135 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
136 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
137 { NULL, 0, 0, 0, NULL, NULL, NULL }
140 typedef void (*Command)(WebKitWebView*, GArray *argv);
142 /* --- UTILITY FUNCTIONS --- */
148 snprintf(tmp, sizeof(tmp), "%i", val);
149 return g_strdup(tmp);
153 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
156 str_replace (const char* search, const char* replace, const char* string) {
157 return g_strjoinv (replace, g_strsplit (string, search, -1));
161 read_file_by_line (gchar *path) {
162 GIOChannel *chan = NULL;
163 gchar *readbuf = NULL;
168 chan = g_io_channel_new_file(path, "r", NULL);
171 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
172 == G_IO_STATUS_NORMAL) {
173 lines[i] = g_strdup (readbuf);
178 g_io_channel_unref (chan);
180 fprintf(stderr, "File '%s' not be read.\n", path);
188 gchar* parseenv (const char* string) {
189 extern char** environ;
190 gchar* newstring = g_strdup (string);
193 while (environ[i] != NULL) {
194 gchar** env = g_strsplit (environ[i], "=", 0);
195 gchar* envname = malloc (strlen (env[0]) + 1);
197 strcat (envname, "$");
198 strcat (envname, env[0]);
200 newstring = str_replace(envname, env[1], newstring);
203 //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
211 setup_signal(int signr, sigfunc *shandler) {
212 struct sigaction nh, oh;
214 nh.sa_handler = shandler;
215 sigemptyset(&nh.sa_mask);
218 if(sigaction(signr, &nh, &oh) < 0)
226 if (uzbl.behave.fifo_dir)
227 unlink (uzbl.comm.fifo_path);
228 if (uzbl.behave.socket_dir)
229 unlink (uzbl.comm.socket_path);
231 g_string_free(uzbl.state.keycmd, TRUE);
232 g_hash_table_destroy(uzbl.bindings);
233 g_hash_table_destroy(uzbl.behave.commands);
237 /* --- SIGNAL HANDLER --- */
240 catch_sigterm(int s) {
246 catch_sigint(int s) {
252 /* --- CALLBACKS --- */
255 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
258 (void) navigation_action;
259 (void) policy_decision;
261 const gchar* uri = webkit_network_request_get_uri (request);
262 if (uzbl.state.verbose)
263 printf("New window requested -> %s \n", uri);
264 new_window_load_uri(uri);
269 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
273 if (uzbl.state.selected_url[0]!=0) {
274 if (uzbl.state.verbose)
275 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
276 new_window_load_uri(uzbl.state.selected_url);
278 if (uzbl.state.verbose)
279 printf("New web view -> %s\n","Nothing to open, exiting");
285 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
288 if (uzbl.behave.download_handler) {
289 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
290 if (uzbl.state.verbose)
291 printf("Download -> %s\n",uri);
292 /* if urls not escaped, we may have to escape and quote uri before this call */
293 run_handler(uzbl.behave.download_handler, uri);
298 /* scroll a bar in a given direction */
300 scroll (GtkAdjustment* bar, GArray *argv) {
304 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
305 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
306 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
309 static void scroll_begin(WebKitWebView* page, GArray *argv) {
310 (void) page; (void) argv;
311 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
314 static void scroll_end(WebKitWebView* page, GArray *argv) {
315 (void) page; (void) argv;
316 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
317 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
320 static void scroll_vert(WebKitWebView* page, GArray *argv) {
322 scroll(uzbl.gui.bar_v, argv);
325 static void scroll_horz(WebKitWebView* page, GArray *argv) {
327 scroll(uzbl.gui.bar_h, argv);
332 if (!uzbl.behave.show_status) {
333 gtk_widget_hide(uzbl.gui.mainbar);
335 gtk_widget_show(uzbl.gui.mainbar);
341 toggle_status_cb (WebKitWebView* page, GArray *argv) {
345 if (uzbl.behave.show_status) {
346 gtk_widget_hide(uzbl.gui.mainbar);
348 gtk_widget_show(uzbl.gui.mainbar);
350 uzbl.behave.show_status = !uzbl.behave.show_status;
355 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
359 //Set selected_url state variable
360 uzbl.state.selected_url[0] = '\0';
362 strcpy (uzbl.state.selected_url, link);
368 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
372 if (uzbl.gui.main_title)
373 g_free (uzbl.gui.main_title);
374 uzbl.gui.main_title = g_strdup (title);
379 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
382 uzbl.gui.sbar.load_progress = progress;
387 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
391 if (uzbl.behave.load_finish_handler)
392 run_handler(uzbl.behave.load_finish_handler, "");
396 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
400 if (uzbl.behave.load_start_handler)
401 run_handler(uzbl.behave.load_start_handler, "");
405 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
408 free (uzbl.state.uri);
409 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
410 uzbl.state.uri = g_string_free (newuri, FALSE);
411 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
412 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
415 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
416 if (uzbl.behave.load_commit_handler)
417 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
421 destroy_cb (GtkWidget* widget, gpointer data) {
429 if (uzbl.behave.history_handler) {
431 struct tm * timeinfo;
434 timeinfo = localtime ( &rawtime );
435 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
436 run_handler(uzbl.behave.history_handler, date);
441 /* VIEW funcs (little webkit wrappers) */
442 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
444 VIEWFUNC(reload_bypass_cache)
445 VIEWFUNC(stop_loading)
452 /* -- command to callback/function map for things we cannot attach to any signals */
454 static struct {char *name; Command command[2];} cmdlist[] =
455 { /* key function no_split */
456 { "back", {view_go_back, 0} },
457 { "forward", {view_go_forward, 0} },
458 { "scroll_vert", {scroll_vert, 0} },
459 { "scroll_horz", {scroll_horz, 0} },
460 { "scroll_begin", {scroll_begin, 0} },
461 { "scroll_end", {scroll_end, 0} },
462 { "reload", {view_reload, 0}, },
463 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
464 { "stop", {view_stop_loading, 0}, },
465 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
466 { "zoom_out", {view_zoom_out, 0}, },
467 { "uri", {load_uri, NOSPLIT} },
468 { "js", {run_js, NOSPLIT} },
469 { "script", {run_external_js, 0} },
470 { "toggle_status", {toggle_status_cb, 0} },
471 { "spawn", {spawn, 0} },
472 { "sh", {spawn_sh, 0} },
473 { "exit", {close_uzbl, 0} },
474 { "search", {search_forward_text, NOSPLIT} },
475 { "search_reverse", {search_reverse_text, NOSPLIT} },
476 { "insert_mode", {toggle_insert_mode, 0} },
477 { "runcmd", {runcmd, NOSPLIT} }
484 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
486 for (i = 0; i < LENGTH(cmdlist); i++)
487 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
490 /* -- CORE FUNCTIONS -- */
493 free_action(gpointer act) {
494 Action *action = (Action*)act;
495 g_free(action->name);
497 g_free(action->param);
502 new_action(const gchar *name, const gchar *param) {
503 Action *action = g_new(Action, 1);
505 action->name = g_strdup(name);
507 action->param = g_strdup(param);
509 action->param = NULL;
515 file_exists (const char * filename) {
516 return (access(filename, F_OK) == 0);
520 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
524 if (argv_idx(argv, 0)) {
525 if (strcmp (argv_idx(argv, 0), "0") == 0) {
526 uzbl.behave.insert_mode = FALSE;
528 uzbl.behave.insert_mode = TRUE;
531 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
538 load_uri (WebKitWebView *web_view, GArray *argv) {
539 if (argv_idx(argv, 0)) {
540 GString* newuri = g_string_new (argv_idx(argv, 0));
541 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
542 g_string_prepend (newuri, "http://");
543 /* if we do handle cookies, ask our handler for them */
544 webkit_web_view_load_uri (web_view, newuri->str);
545 g_string_free (newuri, TRUE);
550 run_js (WebKitWebView * web_view, GArray *argv) {
551 if (argv_idx(argv, 0))
552 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
556 run_external_js (WebKitWebView * web_view, GArray *argv) {
557 if (argv_idx(argv, 0)) {
558 gchar** lines = read_file_by_line (argv_idx (argv, 0));
562 if (lines[0] != NULL) {
563 for (i = 0; lines[i] != NULL; i ++) {
565 js = g_strdup (lines[i]);
567 gchar* newjs = g_strconcat (js, lines[i], NULL);
570 //g_free (lines[i]); - Another mysterious breakage
573 if (uzbl.state.verbose)
574 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
576 if (argv_idx (argv, 1)) {
577 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
580 webkit_web_view_execute_script (web_view, js);
583 fprintf(stderr, "JavaScript file '%s' not be read.\n", argv_idx(argv, 0));
589 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
590 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
591 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
593 if (uzbl.state.searchtx != NULL) {
594 if (uzbl.state.verbose)
595 printf ("Searching: %s\n", uzbl.state.searchtx);
597 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
598 webkit_web_view_unmark_text_matches (page);
599 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
601 if (uzbl.state.searchold != NULL)
602 g_free (uzbl.state.searchold);
604 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
607 webkit_web_view_set_highlight_text_matches (page, TRUE);
608 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
613 search_forward_text (WebKitWebView *page, GArray *argv) {
614 search_text(page, argv, TRUE);
618 search_reverse_text (WebKitWebView *page, GArray *argv) {
619 search_text(page, argv, FALSE);
623 new_window_load_uri (const gchar * uri) {
624 GString* to_execute = g_string_new ("");
625 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
627 for (i = 0; entries[i].long_name != NULL; i++) {
628 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
629 gchar** str = (gchar**)entries[i].arg_data;
631 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
635 if (uzbl.state.verbose)
636 printf("\n%s\n", to_execute->str);
637 g_spawn_command_line_async (to_execute->str, NULL);
638 g_string_free (to_execute, TRUE);
642 close_uzbl (WebKitWebView *page, GArray *argv) {
648 /* --Statusbar functions-- */
650 build_progressbar_ascii(int percent) {
654 GString *bar = g_string_new("");
656 l = (double)percent*((double)width/100.);
657 l = (int)(l+.5)>=(int)l ? l+.5 : l;
659 for(i=0; i<(int)l; i++)
660 g_string_append(bar, "=");
663 g_string_append(bar, "·");
665 return g_string_free(bar, FALSE);
670 const GScannerConfig scan_config = {
673 ) /* cset_skip_characters */,
678 ) /* cset_identifier_first */,
685 ) /* cset_identifier_nth */,
686 ( "" ) /* cpair_comment_single */,
688 TRUE /* case_sensitive */,
690 FALSE /* skip_comment_multi */,
691 FALSE /* skip_comment_single */,
692 FALSE /* scan_comment_multi */,
693 TRUE /* scan_identifier */,
694 TRUE /* scan_identifier_1char */,
695 FALSE /* scan_identifier_NULL */,
696 TRUE /* scan_symbols */,
697 FALSE /* scan_binary */,
698 FALSE /* scan_octal */,
699 FALSE /* scan_float */,
700 FALSE /* scan_hex */,
701 FALSE /* scan_hex_dollar */,
702 FALSE /* scan_string_sq */,
703 FALSE /* scan_string_dq */,
704 TRUE /* numbers_2_int */,
705 FALSE /* int_2_float */,
706 FALSE /* identifier_2_string */,
707 FALSE /* char_2_token */,
708 FALSE /* symbol_2_token */,
709 TRUE /* scope_0_fallback */,
714 uzbl.scan = g_scanner_new(&scan_config);
715 while(symp->symbol_name) {
716 g_scanner_scope_add_symbol(uzbl.scan, 0,
718 GINT_TO_POINTER(symp->symbol_token));
724 expand_template(const char *template) {
725 if(!template) return NULL;
727 GTokenType token = G_TOKEN_NONE;
728 GString *ret = g_string_new("");
732 g_scanner_input_text(uzbl.scan, template, strlen(template));
733 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
734 token = g_scanner_get_next_token(uzbl.scan);
736 if(token == G_TOKEN_SYMBOL) {
737 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
742 g_markup_printf_escaped("%s", uzbl.state.uri):"");
745 buf = itos(uzbl.gui.sbar.load_progress);
746 g_string_append(ret, buf);
749 case SYM_LOADPRGSBAR:
750 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
751 g_string_append(ret, buf);
757 g_markup_printf_escaped("%s", uzbl.gui.main_title):"");
759 case SYM_SELECTED_URI:
761 uzbl.state.selected_url?
762 g_markup_printf_escaped("%s", uzbl.state.selected_url):"");
765 buf = itos(uzbl.xwin);
767 uzbl.state.instance_name?uzbl.state.instance_name:buf);
772 uzbl.state.keycmd->str ?
773 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):"");
777 uzbl.behave.insert_mode?"[I]":"[C]");
781 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
785 buf = itos(WEBKIT_MAJOR_VERSION);
786 g_string_append(ret, buf);
790 buf = itos(WEBKIT_MINOR_VERSION);
791 g_string_append(ret, buf);
795 buf = itos(WEBKIT_MICRO_VERSION);
796 g_string_append(ret, buf);
800 g_string_append(ret, uzbl.state.unameinfo.sysname);
803 g_string_append(ret, uzbl.state.unameinfo.nodename);
806 g_string_append(ret, uzbl.state.unameinfo.release);
809 g_string_append(ret, uzbl.state.unameinfo.version);
812 g_string_append(ret, uzbl.state.unameinfo.machine);
815 g_string_append(ret, ARCH);
819 g_string_append(ret, uzbl.state.unameinfo.domainname);
823 g_string_append(ret, COMMIT);
829 else if(token == G_TOKEN_INT) {
830 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
831 g_string_append(ret, buf);
834 else if(token == G_TOKEN_IDENTIFIER) {
835 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
837 else if(token == G_TOKEN_CHAR) {
838 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
842 return g_string_free(ret, FALSE);
844 /* --End Statusbar functions-- */
847 sharg_append(GArray *a, const gchar *str) {
848 const gchar *s = (str ? str : "");
849 g_array_append_val(a, s);
852 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
854 run_command (const gchar *command, const guint npre, const gchar **args,
855 const gboolean sync, char **stdout) {
856 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
859 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
860 gchar *pid = itos(getpid());
861 gchar *xwin = itos(uzbl.xwin);
863 sharg_append(a, command);
864 for (i = 0; i < npre; i++) /* add n args before the default vars */
865 sharg_append(a, args[i]);
866 sharg_append(a, uzbl.state.config_file);
867 sharg_append(a, pid);
868 sharg_append(a, xwin);
869 sharg_append(a, uzbl.comm.fifo_path);
870 sharg_append(a, uzbl.comm.socket_path);
871 sharg_append(a, uzbl.state.uri);
872 sharg_append(a, uzbl.gui.main_title);
874 for (i = npre; i < g_strv_length((gchar**)args); i++)
875 sharg_append(a, args[i]);
877 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
878 NULL, NULL, stdout, NULL, NULL, &err);
879 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
880 NULL, NULL, NULL, &err);
882 if (uzbl.state.verbose) {
883 GString *s = g_string_new("spawned:");
884 for (i = 0; i < (a->len); i++) {
885 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
886 g_string_append_printf(s, " %s", qarg);
889 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
890 printf("%s\n", s->str);
891 g_string_free(s, TRUE);
894 g_printerr("error on run_command: %s\n", err->message);
899 g_array_free (a, TRUE);
904 split_quoted(const gchar* src, const gboolean unquote) {
905 /* split on unquoted space, return array of strings;
906 remove a layer of quotes and backslashes if unquote */
907 if (!src) return NULL;
911 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
912 GString *s = g_string_new ("");
916 for (p = src; *p != '\0'; p++) {
917 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
918 else if (*p == '\\') { g_string_append_c(s, *p++);
919 g_string_append_c(s, *p); }
920 else if ((*p == '"') && unquote && !sq) dq = !dq;
921 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
923 else if ((*p == '\'') && unquote && !dq) sq = !sq;
924 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
926 else if ((*p == ' ') && !dq && !sq) {
927 dup = g_strdup(s->str);
928 g_array_append_val(a, dup);
929 g_string_truncate(s, 0);
930 } else g_string_append_c(s, *p);
932 dup = g_strdup(s->str);
933 g_array_append_val(a, dup);
934 ret = (gchar**)a->data;
935 g_array_free (a, FALSE);
936 g_string_free (s, FALSE);
941 spawn(WebKitWebView *web_view, GArray *argv) {
943 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
944 if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
948 spawn_sh(WebKitWebView *web_view, GArray *argv) {
950 if (!uzbl.behave.shell_cmd) {
951 g_printerr ("spawn_sh: shell_cmd is not set!\n");
956 gchar *spacer = g_strdup("");
957 g_array_insert_val(argv, 1, spacer);
958 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
960 for (i = 1; i < g_strv_length(cmd); i++)
961 g_array_prepend_val(argv, cmd[i]);
963 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
969 parse_command(const char *cmd, const char *param) {
972 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
975 gchar **par = split_quoted(param, TRUE);
976 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
978 if (c[1]) { /* don't split */
979 sharg_append(a, param);
981 for (i = 0; i < g_strv_length(par); i++)
982 sharg_append(a, par[i]);
984 c[0](uzbl.gui.web_view, a);
986 g_array_free (a, TRUE);
989 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
995 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
996 G_REGEX_OPTIMIZE, 0, NULL);
997 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
998 G_REGEX_OPTIMIZE, 0, NULL);
999 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1000 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1001 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1002 G_REGEX_OPTIMIZE, 0, NULL);
1003 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1004 G_REGEX_OPTIMIZE, 0, NULL);
1008 get_var_value(gchar *name) {
1011 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1012 if(var_is("uri", name)
1013 || var_is("status_message", name)
1014 || var_is("status_format", name)
1015 || var_is("status_background", name)
1016 || var_is("title_format_short", name)
1017 || var_is("title_format_long", name)
1018 || var_is("modkey", name)
1019 || var_is("load_finish_handler", name)
1020 || var_is("load_start_handler", name)
1021 || var_is("load_commit_handler", name)
1022 || var_is("history_handler", name)
1023 || var_is("download_handler", name)
1024 || var_is("cookie_handler", name)
1025 || var_is("fifo_dir", name)
1026 || var_is("socket_dir", name)
1027 || var_is("shell_cmd", name)
1028 || var_is("proxy_url", name)
1029 || var_is("useragent", name))
1031 printf("VAR: %s VALUE: %s\n", name, (char *)*p);
1032 } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
1041 if(*uzbl.net.proxy_url == ' '
1042 || uzbl.net.proxy_url == NULL) {
1043 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1044 (GType) SOUP_SESSION_PROXY_URI);
1047 suri = soup_uri_new(uzbl.net.proxy_url);
1048 g_object_set(G_OBJECT(uzbl.net.soup_session),
1049 SOUP_SESSION_PROXY_URI,
1051 soup_uri_free(suri);
1059 gtk_widget_ref(uzbl.gui.scrolled_win);
1060 gtk_widget_ref(uzbl.gui.mainbar);
1061 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1062 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1064 if(uzbl.behave.status_top) {
1065 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1066 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1069 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1070 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1072 gtk_widget_unref(uzbl.gui.scrolled_win);
1073 gtk_widget_unref(uzbl.gui.mainbar);
1074 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1078 var_is(const char *x, const char *y) {
1079 return (strcmp(x, y) == 0 ? TRUE : FALSE );
1083 set_var_value(gchar *name, gchar *val) {
1087 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1088 if(var_is("status_message", name)
1089 || var_is("status_background", name)
1090 || var_is("status_format", name)
1091 || var_is("title_format_long", name)
1092 || var_is("title_format_short", name)
1093 || var_is("load_finish_handler", name)
1094 || var_is("load_start_handler", name)
1095 || var_is("load_commit_handler", name)
1096 || var_is("history_handler", name)
1097 || var_is("download_handler", name)
1098 || var_is("cookie_handler", name)) {
1104 else if(var_is("uri", name)) {
1107 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1108 g_array_append_val(a, *p);
1109 load_uri(uzbl.gui.web_view, a);
1110 g_array_free(a, TRUE);
1112 else if(var_is("proxy_url", name)) {
1117 else if(var_is("fifo_dir", name)) {
1119 *p = init_fifo(g_strdup(val));
1121 else if(var_is("socket_dir", name)) {
1123 *p = init_socket(g_strdup(val));
1125 else if(var_is("modkey", name)) {
1128 *p = g_utf8_strup(val, -1);
1129 uzbl.behave.modmask = 0;
1130 for (i = 0; modkeys[i].key != NULL; i++) {
1131 if (g_strrstr(*p, modkeys[i].key))
1132 uzbl.behave.modmask |= modkeys[i].mask;
1135 else if(var_is("useragent", name)) {
1137 *p = set_useragent(g_strdup(val));
1139 else if(var_is("shell_cmd", name)) {
1143 /* variables that take int values */
1146 *ip = (int)strtoul(val, &endp, 10);
1148 if(var_is("show_status", name)) {
1151 else if(var_is("always_insert_mode", name)) {
1152 uzbl.behave.insert_mode =
1153 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1156 else if (var_is("max_conns", name)) {
1157 g_object_set(G_OBJECT(uzbl.net.soup_session),
1158 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1160 else if (var_is("max_conns_host", name)) {
1161 g_object_set(G_OBJECT(uzbl.net.soup_session),
1162 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1164 else if (var_is("http_debug", name)) {
1165 //soup_session_remove_feature
1166 // (uzbl.net.soup_session, uzbl.net.soup_logger);
1167 soup_session_remove_feature
1168 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1169 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1170 /*g_free(uzbl.net.soup_logger);*/
1172 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1173 soup_session_add_feature(uzbl.net.soup_session,
1174 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1176 else if (var_is("status_top", name)) {
1179 else if (var_is("default_font_size", name)) {
1180 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1181 g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
1183 else if (var_is("minimum_font_size", name)) {
1184 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1185 g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
1193 runcmd(WebKitWebView* page, GArray *argv) {
1195 parse_cmd_line(argv_idx(argv, 0));
1199 parse_cmd_line(const char *ctl_line) {
1203 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1204 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1205 if(tokens[0][0] == 0) {
1206 gchar* value = parseenv (tokens[2]);
1207 set_var_value(tokens[1], value);
1212 printf("Error in command: %s\n", tokens[0]);
1215 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1216 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1217 if(tokens[0][0] == 0) {
1218 get_var_value(tokens[1]);
1222 printf("Error in command: %s\n", tokens[0]);
1225 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1226 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1227 if(tokens[0][0] == 0) {
1228 gchar* value = parseenv (tokens[2]);
1229 add_binding(tokens[1], value);
1234 printf("Error in command: %s\n", tokens[0]);
1237 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1238 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1239 if(tokens[0][0] == 0) {
1240 parse_command(tokens[1], tokens[2]);
1244 printf("Error in command: %s\n", tokens[0]);
1246 /* KEYCMD command */
1247 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1248 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1249 if(tokens[0][0] == 0) {
1250 /* should incremental commands want each individual "keystroke"
1251 sent in a loop or the whole string in one go like now? */
1252 g_string_assign(uzbl.state.keycmd, tokens[1]);
1259 else if( (ctl_line[0] == '#')
1260 || (ctl_line[0] == ' ')
1261 || (ctl_line[0] == '\n'))
1262 ; /* ignore these lines */
1264 printf("Command not understood (%s)\n", ctl_line);
1270 build_stream_name(int type, const gchar* dir) {
1272 State *s = &uzbl.state;
1275 xwin_str = itos((int)uzbl.xwin);
1277 str = g_strdup_printf
1278 ("%s/uzbl_fifo_%s", dir,
1279 s->instance_name ? s->instance_name : xwin_str);
1280 } else if (type == SOCKET) {
1281 str = g_strdup_printf
1282 ("%s/uzbl_socket_%s", dir,
1283 s->instance_name ? s->instance_name : xwin_str );
1290 control_fifo(GIOChannel *gio, GIOCondition condition) {
1291 if (uzbl.state.verbose)
1292 printf("triggered\n");
1297 if (condition & G_IO_HUP)
1298 g_error ("Fifo: Read end of pipe died!\n");
1301 g_error ("Fifo: GIOChannel broke\n");
1303 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1304 if (ret == G_IO_STATUS_ERROR) {
1305 g_error ("Fifo: Error reading: %s\n", err->message);
1309 parse_cmd_line(ctl_line);
1316 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1317 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1318 if (unlink(uzbl.comm.fifo_path) == -1)
1319 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1320 g_free(uzbl.comm.fifo_path);
1321 uzbl.comm.fifo_path = NULL;
1324 if (*dir == ' ') { /* space unsets the variable */
1329 GIOChannel *chan = NULL;
1330 GError *error = NULL;
1331 gchar *path = build_stream_name(FIFO, dir);
1333 if (!file_exists(path)) {
1334 if (mkfifo (path, 0666) == 0) {
1335 // 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.
1336 chan = g_io_channel_new_file(path, "r+", &error);
1338 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1339 if (uzbl.state.verbose)
1340 printf ("init_fifo: created successfully as %s\n", path);
1341 uzbl.comm.fifo_path = path;
1343 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1344 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1345 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1346 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1348 /* if we got this far, there was an error; cleanup */
1349 if (error) g_error_free (error);
1356 control_stdin(GIOChannel *gio, GIOCondition condition) {
1357 gchar *ctl_line = NULL;
1358 gsize ctl_line_len = 0;
1361 if (condition & G_IO_HUP) {
1362 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1366 ret = g_io_channel_read_line(gio, &ctl_line, &ctl_line_len, NULL, NULL);
1367 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1370 parse_cmd_line(ctl_line);
1378 GIOChannel *chan = NULL;
1379 GError *error = NULL;
1381 chan = g_io_channel_unix_new(fileno(stdin));
1383 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1384 g_error ("Stdin: could not add watch\n");
1386 if (uzbl.state.verbose)
1387 printf ("Stdin: watch added successfully\n");
1390 g_error ("Stdin: Error while opening: %s\n", error->message);
1392 if (error) g_error_free (error);
1396 control_socket(GIOChannel *chan) {
1397 struct sockaddr_un remote;
1398 char buffer[512], *ctl_line;
1400 int sock, clientsock, n, done;
1403 sock = g_io_channel_unix_get_fd(chan);
1405 memset (buffer, 0, sizeof (buffer));
1407 t = sizeof (remote);
1408 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1412 memset (temp, 0, sizeof (temp));
1413 n = recv (clientsock, temp, 128, 0);
1415 buffer[strlen (buffer)] = '\0';
1419 strcat (buffer, temp);
1422 if (strcmp (buffer, "\n") < 0) {
1423 buffer[strlen (buffer) - 1] = '\0';
1425 buffer[strlen (buffer)] = '\0';
1428 ctl_line = g_strdup(buffer);
1429 parse_cmd_line (ctl_line);
1432 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1433 GError *error = NULL;
1436 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1437 if (ret == G_IO_STATUS_ERROR)
1438 g_error ("Error reading: %s\n", error->message);
1440 printf("Got line %s (%u bytes) \n",ctl_line, len);
1442 parse_line(ctl_line);
1450 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1451 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1452 if (unlink(uzbl.comm.socket_path) == -1)
1453 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1454 g_free(uzbl.comm.socket_path);
1455 uzbl.comm.socket_path = NULL;
1463 GIOChannel *chan = NULL;
1465 struct sockaddr_un local;
1466 gchar *path = build_stream_name(SOCKET, dir);
1468 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1470 local.sun_family = AF_UNIX;
1471 strcpy (local.sun_path, path);
1472 unlink (local.sun_path);
1474 len = strlen (local.sun_path) + sizeof (local.sun_family);
1475 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1476 if (uzbl.state.verbose)
1477 printf ("init_socket: opened in %s\n", path);
1480 if( (chan = g_io_channel_unix_new(sock)) ) {
1481 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1482 uzbl.comm.socket_path = path;
1485 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1487 /* if we got this far, there was an error; cleanup */
1494 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1495 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1497 // this function may be called very early when the templates are not set (yet), hence the checks
1499 update_title (void) {
1500 Behaviour *b = &uzbl.behave;
1503 if (b->show_status) {
1504 if (b->title_format_short) {
1505 parsed = expand_template(b->title_format_short);
1506 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1509 if (b->status_format) {
1510 parsed = expand_template(b->status_format);
1511 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1514 if (b->status_background) {
1516 gdk_color_parse (b->status_background, &color);
1517 //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)
1518 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1521 if (b->title_format_long) {
1522 parsed = expand_template(b->title_format_long);
1523 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1530 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1532 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1536 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1537 || 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)
1540 /* turn off insert mode (if always_insert_mode is not used) */
1541 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1542 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1547 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1550 if (event->keyval == GDK_Escape) {
1551 g_string_truncate(uzbl.state.keycmd, 0);
1556 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1557 if (event->keyval == GDK_Insert) {
1559 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1560 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1562 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1565 g_string_append (uzbl.state.keycmd, str);
1572 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1573 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1577 gboolean key_ret = FALSE;
1578 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1580 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1582 run_keycmd(key_ret);
1584 if (key_ret) return (!uzbl.behave.insert_mode);
1589 run_keycmd(const gboolean key_ret) {
1590 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1592 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1593 g_string_truncate(uzbl.state.keycmd, 0);
1594 parse_command(action->name, action->param);
1598 /* try if it's an incremental keycmd or one that takes args, and run it */
1599 GString* short_keys = g_string_new ("");
1600 GString* short_keys_inc = g_string_new ("");
1602 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1603 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1604 g_string_assign(short_keys_inc, short_keys->str);
1605 g_string_append_c(short_keys, '_');
1606 g_string_append_c(short_keys_inc, '*');
1608 gboolean exec_now = FALSE;
1609 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1610 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1611 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1612 if (key_ret) { /* just quit the incremental command on return */
1613 g_string_truncate(uzbl.state.keycmd, 0);
1615 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1619 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1620 GString* actionname = g_string_new ("");
1621 GString* actionparam = g_string_new ("");
1622 g_string_erase (parampart, 0, i+1);
1624 g_string_printf (actionname, action->name, parampart->str);
1626 g_string_printf (actionparam, action->param, parampart->str);
1627 parse_command(actionname->str, actionparam->str);
1628 g_string_free (actionname, TRUE);
1629 g_string_free (actionparam, TRUE);
1630 g_string_free (parampart, TRUE);
1632 g_string_truncate(uzbl.state.keycmd, 0);
1636 g_string_truncate(short_keys, short_keys->len - 1);
1638 g_string_free (short_keys, TRUE);
1639 g_string_free (short_keys_inc, TRUE);
1646 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1647 //main_window_ref = g_object_ref(scrolled_window);
1648 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
1650 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1651 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1653 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1654 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1655 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1656 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1657 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1658 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1659 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1660 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1661 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1662 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1663 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1665 return scrolled_window;
1672 g->mainbar = gtk_hbox_new (FALSE, 0);
1674 /* keep a reference to the bar so we can re-pack it at runtime*/
1675 //sbar_ref = g_object_ref(g->mainbar);
1677 g->mainbar_label = gtk_label_new ("");
1678 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1679 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1680 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1681 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1682 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1687 GtkWidget* create_window () {
1688 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1689 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1690 gtk_widget_set_name (window, "Uzbl browser");
1691 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1697 run_handler (const gchar *act, const gchar *args) {
1698 char **parts = g_strsplit(act, " ", 2);
1700 else if ((g_strcmp0(parts[0], "spawn") == 0)
1701 || (g_strcmp0(parts[0], "sh") == 0)) {
1703 GString *a = g_string_new ("");
1705 spawnparts = split_quoted(parts[1], FALSE);
1706 g_string_append_printf(a, "%s", spawnparts[0]);
1707 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1708 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1709 g_string_append_printf(a, " %s", spawnparts[i]);
1710 parse_command(parts[0], a->str);
1711 g_string_free (a, TRUE);
1712 g_strfreev (spawnparts);
1714 parse_command(parts[0], parts[1]);
1719 add_binding (const gchar *key, const gchar *act) {
1720 char **parts = g_strsplit(act, " ", 2);
1727 if (uzbl.state.verbose)
1728 printf ("Binding %-10s : %s\n", key, act);
1729 action = new_action(parts[0], parts[1]);
1731 if(g_hash_table_lookup(uzbl.bindings, key))
1732 g_hash_table_remove(uzbl.bindings, key);
1733 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1739 get_xdg_var (XDG_Var xdg) {
1740 const gchar* actual_value = getenv (xdg.environmental);
1741 const gchar* home = getenv ("HOME");
1743 gchar* return_value = str_replace ("~", home, g_strdup (actual_value));
1745 if (! actual_value || strcmp (actual_value, "") == 0) {
1746 if (xdg.default_value) {
1747 return_value = str_replace ("~", home, g_strdup (xdg.default_value));
1749 return_value = NULL;
1753 return return_value;
1757 find_xdg_file (int xdg_type, char* filename) {
1758 /* xdg_type = 0 => config
1759 xdg_type = 1 => data
1760 xdg_type = 2 => cache*/
1762 gchar* temporary_file = (char *)malloc (1024);
1763 gchar* temporary_string = NULL;
1766 strcpy (temporary_file, get_xdg_var (XDG[xdg_type]));
1768 strcat (temporary_file, filename);
1770 if (! file_exists (temporary_file) && xdg_type != 2) {
1771 temporary_string = (char *) strtok_r (get_xdg_var (XDG[3 + xdg_type]), ":", &saveptr);
1773 while (temporary_string && ! file_exists (temporary_file)) {
1774 strcpy (temporary_file, temporary_string);
1775 strcat (temporary_file, filename);
1776 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1780 if (file_exists (temporary_file)) {
1781 return temporary_file;
1789 State *s = &uzbl.state;
1790 Network *n = &uzbl.net;
1792 uzbl.behave.reset_command_mode = 1;
1794 if (!s->config_file) {
1795 s->config_file = g_strdup (find_xdg_file (0, "/uzbl/config"));
1798 if (s->config_file) {
1799 GIOChannel *chan = NULL;
1800 gchar *readbuf = NULL;
1803 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1806 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1807 == G_IO_STATUS_NORMAL) {
1808 parse_cmd_line(readbuf);
1812 g_io_channel_unref (chan);
1813 if (uzbl.state.verbose)
1814 printf ("Config %s loaded\n", s->config_file);
1816 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1819 if (uzbl.state.verbose)
1820 printf ("No configuration file loaded.\n");
1822 if (!uzbl.behave.status_format)
1823 set_var_value("status_format", STATUS_DEFAULT);
1824 if (!uzbl.behave.title_format_long)
1825 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1826 if (!uzbl.behave.title_format_short)
1827 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1830 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1834 set_useragent(gchar *val) {
1839 gchar *ua = expand_template(val);
1841 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1845 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1848 if (!uzbl.behave.cookie_handler) return;
1850 gchar * stdout = NULL;
1851 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1852 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1853 gchar *action = g_strdup ("GET");
1854 SoupURI * soup_uri = soup_message_get_uri(msg);
1855 sharg_append(a, action);
1856 sharg_append(a, soup_uri->host);
1857 sharg_append(a, soup_uri->path);
1858 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1859 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1861 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1864 g_array_free(a, TRUE);
1868 save_cookies (SoupMessage *msg, gpointer user_data){
1872 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1873 cookie = soup_cookie_to_set_cookie_header(ck->data);
1874 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1875 SoupURI * soup_uri = soup_message_get_uri(msg);
1876 gchar *action = strdup("PUT");
1877 sharg_append(a, action);
1878 sharg_append(a, soup_uri->host);
1879 sharg_append(a, soup_uri->path);
1880 sharg_append(a, cookie);
1881 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1884 g_array_free(a, TRUE);
1890 main (int argc, char* argv[]) {
1891 gtk_init (&argc, &argv);
1892 if (!g_thread_supported ())
1893 g_thread_init (NULL);
1895 strcpy(uzbl.state.executable_path,argv[0]);
1897 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1898 g_option_context_add_main_entries (context, entries, NULL);
1899 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1900 g_option_context_parse (context, &argc, &argv, NULL);
1901 /* initialize hash table */
1902 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1904 uzbl.net.soup_session = webkit_get_default_session();
1905 uzbl.state.keycmd = g_string_new("");
1907 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1908 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1909 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1910 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1912 if(uname(&uzbl.state.unameinfo) == -1)
1913 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1918 make_var_to_name_hash();
1920 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1922 uzbl.gui.scrolled_win = create_browser();
1925 /* initial packing */
1926 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1927 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1929 uzbl.gui.main_window = create_window ();
1930 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1932 //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1934 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1935 gtk_widget_show_all (uzbl.gui.main_window);
1936 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1938 if (uzbl.state.verbose) {
1939 printf("Uzbl start location: %s\n", argv[0]);
1940 printf("window_id %i\n",(int) uzbl.xwin);
1941 printf("pid %i\n", getpid ());
1942 printf("name: %s\n", uzbl.state.instance_name);
1945 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1946 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1947 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1948 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1949 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1953 if (!uzbl.behave.show_status)
1954 gtk_widget_hide(uzbl.gui.mainbar);
1960 //if(uzbl.state.uri)
1961 // load_uri (uzbl.gui.web_view, uzbl.state.uri);
1967 return EXIT_SUCCESS;
1970 /* vi: set et ts=4: */