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) {
168 return g_strjoinv (replace, g_strsplit (string, search, -1));
172 read_file_by_line (gchar *path) {
173 GIOChannel *chan = NULL;
174 gchar *readbuf = NULL;
176 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
179 chan = g_io_channel_new_file(path, "r", NULL);
182 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
183 const gchar* val = g_strdup (readbuf);
184 g_array_append_val (lines, val);
189 g_io_channel_unref (chan);
191 fprintf(stderr, "File '%s' not be read.\n", path);
198 gchar* parseenv (const char* string) {
199 extern char** environ;
200 gchar* newstring = g_strdup (string);
203 while (environ[i] != NULL) {
204 gchar** env = g_strsplit (environ[i], "=", 0);
205 gchar* envname = malloc (strlen (env[0]) + 1);
207 strcat (envname, "$");
208 strcat (envname, env[0]);
210 newstring = str_replace(envname, env[1], newstring);
213 //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
221 setup_signal(int signr, sigfunc *shandler) {
222 struct sigaction nh, oh;
224 nh.sa_handler = shandler;
225 sigemptyset(&nh.sa_mask);
228 if(sigaction(signr, &nh, &oh) < 0)
236 if (uzbl.behave.fifo_dir)
237 unlink (uzbl.comm.fifo_path);
238 if (uzbl.behave.socket_dir)
239 unlink (uzbl.comm.socket_path);
241 g_free(uzbl.state.executable_path);
242 g_string_free(uzbl.state.keycmd, TRUE);
243 g_hash_table_destroy(uzbl.bindings);
244 g_hash_table_destroy(uzbl.behave.commands);
248 /* --- SIGNAL HANDLER --- */
251 catch_sigterm(int s) {
257 catch_sigint(int s) {
263 /* --- CALLBACKS --- */
266 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
269 (void) navigation_action;
270 (void) policy_decision;
272 const gchar* uri = webkit_network_request_get_uri (request);
273 if (uzbl.state.verbose)
274 printf("New window requested -> %s \n", uri);
275 new_window_load_uri(uri);
280 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
284 if (uzbl.state.selected_url != NULL) {
285 if (uzbl.state.verbose)
286 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
287 new_window_load_uri(uzbl.state.selected_url);
289 if (uzbl.state.verbose)
290 printf("New web view -> %s\n","Nothing to open, exiting");
296 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
299 if (uzbl.behave.download_handler) {
300 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
301 if (uzbl.state.verbose)
302 printf("Download -> %s\n",uri);
303 /* if urls not escaped, we may have to escape and quote uri before this call */
304 run_handler(uzbl.behave.download_handler, uri);
309 /* scroll a bar in a given direction */
311 scroll (GtkAdjustment* bar, GArray *argv) {
315 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
316 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
317 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
320 static void scroll_begin(WebKitWebView* page, GArray *argv) {
321 (void) page; (void) argv;
322 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
325 static void scroll_end(WebKitWebView* page, GArray *argv) {
326 (void) page; (void) argv;
327 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
328 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
331 static void scroll_vert(WebKitWebView* page, GArray *argv) {
333 scroll(uzbl.gui.bar_v, argv);
336 static void scroll_horz(WebKitWebView* page, GArray *argv) {
338 scroll(uzbl.gui.bar_h, argv);
343 if (!uzbl.behave.show_status) {
344 gtk_widget_hide(uzbl.gui.mainbar);
346 gtk_widget_show(uzbl.gui.mainbar);
352 toggle_status_cb (WebKitWebView* page, GArray *argv) {
356 if (uzbl.behave.show_status) {
357 gtk_widget_hide(uzbl.gui.mainbar);
359 gtk_widget_show(uzbl.gui.mainbar);
361 uzbl.behave.show_status = !uzbl.behave.show_status;
366 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
370 //Set selected_url state variable
371 g_free(uzbl.state.selected_url);
372 uzbl.state.selected_url = NULL;
374 uzbl.state.selected_url = g_strdup(link);
380 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
384 if (uzbl.gui.main_title)
385 g_free (uzbl.gui.main_title);
386 uzbl.gui.main_title = g_strdup (title);
391 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
394 uzbl.gui.sbar.load_progress = progress;
399 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
403 if (uzbl.behave.load_finish_handler)
404 run_handler(uzbl.behave.load_finish_handler, "");
408 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
412 if (uzbl.behave.load_start_handler)
413 run_handler(uzbl.behave.load_start_handler, "");
417 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
420 free (uzbl.state.uri);
421 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
422 uzbl.state.uri = g_string_free (newuri, FALSE);
423 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
424 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
427 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
428 if (uzbl.behave.load_commit_handler)
429 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
433 destroy_cb (GtkWidget* widget, gpointer data) {
441 if (uzbl.behave.history_handler) {
443 struct tm * timeinfo;
446 timeinfo = localtime ( &rawtime );
447 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
448 run_handler(uzbl.behave.history_handler, date);
453 /* VIEW funcs (little webkit wrappers) */
454 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
456 VIEWFUNC(reload_bypass_cache)
457 VIEWFUNC(stop_loading)
464 /* -- command to callback/function map for things we cannot attach to any signals */
466 static struct {char *name; Command command[2];} cmdlist[] =
467 { /* key function no_split */
468 { "back", {view_go_back, 0} },
469 { "forward", {view_go_forward, 0} },
470 { "scroll_vert", {scroll_vert, 0} },
471 { "scroll_horz", {scroll_horz, 0} },
472 { "scroll_begin", {scroll_begin, 0} },
473 { "scroll_end", {scroll_end, 0} },
474 { "reload", {view_reload, 0}, },
475 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
476 { "stop", {view_stop_loading, 0}, },
477 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
478 { "zoom_out", {view_zoom_out, 0}, },
479 { "uri", {load_uri, NOSPLIT} },
480 { "js", {run_js, NOSPLIT} },
481 { "script", {run_external_js, 0} },
482 { "toggle_status", {toggle_status_cb, 0} },
483 { "spawn", {spawn, 0} },
484 { "sh", {spawn_sh, 0} },
485 { "exit", {close_uzbl, 0} },
486 { "search", {search_forward_text, NOSPLIT} },
487 { "search_reverse", {search_reverse_text, NOSPLIT} },
488 { "toggle_insert_mode", {toggle_insert_mode, 0} },
489 { "runcmd", {runcmd, NOSPLIT} }
496 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
498 for (i = 0; i < LENGTH(cmdlist); i++)
499 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
502 /* -- CORE FUNCTIONS -- */
505 free_action(gpointer act) {
506 Action *action = (Action*)act;
507 g_free(action->name);
509 g_free(action->param);
514 new_action(const gchar *name, const gchar *param) {
515 Action *action = g_new(Action, 1);
517 action->name = g_strdup(name);
519 action->param = g_strdup(param);
521 action->param = NULL;
527 file_exists (const char * filename) {
528 return (access(filename, F_OK) == 0);
532 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
536 if (argv_idx(argv, 0)) {
537 if (strcmp (argv_idx(argv, 0), "0") == 0) {
538 uzbl.behave.insert_mode = FALSE;
540 uzbl.behave.insert_mode = TRUE;
543 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
550 load_uri (WebKitWebView *web_view, GArray *argv) {
551 if (argv_idx(argv, 0)) {
552 GString* newuri = g_string_new (argv_idx(argv, 0));
553 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
554 g_string_prepend (newuri, "http://");
555 /* if we do handle cookies, ask our handler for them */
556 webkit_web_view_load_uri (web_view, newuri->str);
557 g_string_free (newuri, TRUE);
562 run_js (WebKitWebView * web_view, GArray *argv) {
563 if (argv_idx(argv, 0))
564 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
568 run_external_js (WebKitWebView * web_view, GArray *argv) {
569 if (argv_idx(argv, 0)) {
570 GArray* lines = read_file_by_line (argv_idx (argv, 0));
575 while ((line = g_array_index(lines, gchar*, i))) {
577 js = g_strdup (line);
579 gchar* newjs = g_strconcat (js, line, NULL);
585 if (uzbl.state.verbose)
586 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
588 if (argv_idx (argv, 1)) {
589 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
593 webkit_web_view_execute_script (web_view, js);
595 g_array_free (lines, TRUE);
600 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
601 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
602 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
604 if (uzbl.state.searchtx != NULL) {
605 if (uzbl.state.verbose)
606 printf ("Searching: %s\n", uzbl.state.searchtx);
608 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
609 webkit_web_view_unmark_text_matches (page);
610 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
612 if (uzbl.state.searchold != NULL)
613 g_free (uzbl.state.searchold);
615 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
618 webkit_web_view_set_highlight_text_matches (page, TRUE);
619 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
620 g_free(uzbl.state.searchtx);
621 uzbl.state.searchtx = NULL;
626 search_forward_text (WebKitWebView *page, GArray *argv) {
627 search_text(page, argv, TRUE);
631 search_reverse_text (WebKitWebView *page, GArray *argv) {
632 search_text(page, argv, FALSE);
636 new_window_load_uri (const gchar * uri) {
637 GString* to_execute = g_string_new ("");
638 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
640 for (i = 0; entries[i].long_name != NULL; i++) {
641 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
642 gchar** str = (gchar**)entries[i].arg_data;
644 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
648 if (uzbl.state.verbose)
649 printf("\n%s\n", to_execute->str);
650 g_spawn_command_line_async (to_execute->str, NULL);
651 g_string_free (to_execute, TRUE);
655 close_uzbl (WebKitWebView *page, GArray *argv) {
661 /* --Statusbar functions-- */
663 build_progressbar_ascii(int percent) {
667 GString *bar = g_string_new("");
669 l = (double)percent*((double)width/100.);
670 l = (int)(l+.5)>=(int)l ? l+.5 : l;
672 for(i=0; i<(int)l; i++)
673 g_string_append(bar, "=");
676 g_string_append(bar, "·");
678 return g_string_free(bar, FALSE);
683 const GScannerConfig scan_config = {
686 ) /* cset_skip_characters */,
691 ) /* cset_identifier_first */,
698 ) /* cset_identifier_nth */,
699 ( "" ) /* cpair_comment_single */,
701 TRUE /* case_sensitive */,
703 FALSE /* skip_comment_multi */,
704 FALSE /* skip_comment_single */,
705 FALSE /* scan_comment_multi */,
706 TRUE /* scan_identifier */,
707 TRUE /* scan_identifier_1char */,
708 FALSE /* scan_identifier_NULL */,
709 TRUE /* scan_symbols */,
710 FALSE /* scan_binary */,
711 FALSE /* scan_octal */,
712 FALSE /* scan_float */,
713 FALSE /* scan_hex */,
714 FALSE /* scan_hex_dollar */,
715 FALSE /* scan_string_sq */,
716 FALSE /* scan_string_dq */,
717 TRUE /* numbers_2_int */,
718 FALSE /* int_2_float */,
719 FALSE /* identifier_2_string */,
720 FALSE /* char_2_token */,
721 FALSE /* symbol_2_token */,
722 TRUE /* scope_0_fallback */,
727 uzbl.scan = g_scanner_new(&scan_config);
728 while(symp->symbol_name) {
729 g_scanner_scope_add_symbol(uzbl.scan, 0,
731 GINT_TO_POINTER(symp->symbol_token));
737 expand_template(const char *template) {
738 if(!template) return NULL;
740 GTokenType token = G_TOKEN_NONE;
741 GString *ret = g_string_new("");
745 g_scanner_input_text(uzbl.scan, template, strlen(template));
746 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
747 token = g_scanner_get_next_token(uzbl.scan);
749 if(token == G_TOKEN_SYMBOL) {
750 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
755 g_markup_printf_escaped("%s", uzbl.state.uri):"");
758 buf = itos(uzbl.gui.sbar.load_progress);
759 g_string_append(ret, buf);
762 case SYM_LOADPRGSBAR:
763 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
764 g_string_append(ret, buf);
770 g_markup_printf_escaped("%s", uzbl.gui.main_title):"");
772 case SYM_SELECTED_URI:
774 uzbl.state.selected_url?
775 g_markup_printf_escaped("%s", uzbl.state.selected_url):"");
778 buf = itos(uzbl.xwin);
780 uzbl.state.instance_name?uzbl.state.instance_name:buf);
785 uzbl.state.keycmd->str ?
786 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):"");
790 uzbl.behave.insert_mode?"[I]":"[C]");
794 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
798 buf = itos(WEBKIT_MAJOR_VERSION);
799 g_string_append(ret, buf);
803 buf = itos(WEBKIT_MINOR_VERSION);
804 g_string_append(ret, buf);
808 buf = itos(WEBKIT_MICRO_VERSION);
809 g_string_append(ret, buf);
813 g_string_append(ret, uzbl.state.unameinfo.sysname);
816 g_string_append(ret, uzbl.state.unameinfo.nodename);
819 g_string_append(ret, uzbl.state.unameinfo.release);
822 g_string_append(ret, uzbl.state.unameinfo.version);
825 g_string_append(ret, uzbl.state.unameinfo.machine);
828 g_string_append(ret, ARCH);
832 g_string_append(ret, uzbl.state.unameinfo.domainname);
836 g_string_append(ret, COMMIT);
842 else if(token == G_TOKEN_INT) {
843 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
844 g_string_append(ret, buf);
847 else if(token == G_TOKEN_IDENTIFIER) {
848 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
850 else if(token == G_TOKEN_CHAR) {
851 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
855 return g_string_free(ret, FALSE);
857 /* --End Statusbar functions-- */
860 sharg_append(GArray *a, const gchar *str) {
861 const gchar *s = (str ? str : "");
862 g_array_append_val(a, s);
865 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
867 run_command (const gchar *command, const guint npre, const gchar **args,
868 const gboolean sync, char **stdout) {
869 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
872 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
873 gchar *pid = itos(getpid());
874 gchar *xwin = itos(uzbl.xwin);
876 sharg_append(a, command);
877 for (i = 0; i < npre; i++) /* add n args before the default vars */
878 sharg_append(a, args[i]);
879 sharg_append(a, uzbl.state.config_file);
880 sharg_append(a, pid);
881 sharg_append(a, xwin);
882 sharg_append(a, uzbl.comm.fifo_path);
883 sharg_append(a, uzbl.comm.socket_path);
884 sharg_append(a, uzbl.state.uri);
885 sharg_append(a, uzbl.gui.main_title);
887 for (i = npre; i < g_strv_length((gchar**)args); i++)
888 sharg_append(a, args[i]);
890 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
891 NULL, NULL, stdout, NULL, NULL, &err);
892 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
893 NULL, NULL, NULL, &err);
895 if (uzbl.state.verbose) {
896 GString *s = g_string_new("spawned:");
897 for (i = 0; i < (a->len); i++) {
898 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
899 g_string_append_printf(s, " %s", qarg);
902 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
903 printf("%s\n", s->str);
904 g_string_free(s, TRUE);
907 g_printerr("error on run_command: %s\n", err->message);
912 g_array_free (a, TRUE);
917 split_quoted(const gchar* src, const gboolean unquote) {
918 /* split on unquoted space, return array of strings;
919 remove a layer of quotes and backslashes if unquote */
920 if (!src) return NULL;
924 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
925 GString *s = g_string_new ("");
929 for (p = src; *p != '\0'; p++) {
930 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
931 else if (*p == '\\') { g_string_append_c(s, *p++);
932 g_string_append_c(s, *p); }
933 else if ((*p == '"') && unquote && !sq) dq = !dq;
934 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
936 else if ((*p == '\'') && unquote && !dq) sq = !sq;
937 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
939 else if ((*p == ' ') && !dq && !sq) {
940 dup = g_strdup(s->str);
941 g_array_append_val(a, dup);
942 g_string_truncate(s, 0);
943 } else g_string_append_c(s, *p);
945 dup = g_strdup(s->str);
946 g_array_append_val(a, dup);
947 ret = (gchar**)a->data;
948 g_array_free (a, FALSE);
949 g_string_free (s, FALSE);
954 spawn(WebKitWebView *web_view, GArray *argv) {
956 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
957 if (argv_idx(argv, 0)) run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
961 spawn_sh(WebKitWebView *web_view, GArray *argv) {
963 if (!uzbl.behave.shell_cmd) {
964 g_printerr ("spawn_sh: shell_cmd is not set!\n");
969 gchar *spacer = g_strdup("");
970 g_array_insert_val(argv, 1, spacer);
971 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
973 for (i = 1; i < g_strv_length(cmd); i++)
974 g_array_prepend_val(argv, cmd[i]);
976 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
982 parse_command(const char *cmd, const char *param) {
985 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
988 gchar **par = split_quoted(param, TRUE);
989 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
991 if (c[1]) { /* don't split */
992 sharg_append(a, param);
994 for (i = 0; i < g_strv_length(par); i++)
995 sharg_append(a, par[i]);
997 c[0](uzbl.gui.web_view, a);
999 g_array_free (a, TRUE);
1002 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1005 /* command parser */
1008 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1009 G_REGEX_OPTIMIZE, 0, NULL);
1010 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1011 G_REGEX_OPTIMIZE, 0, NULL);
1012 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1013 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1014 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1015 G_REGEX_OPTIMIZE, 0, NULL);
1016 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1017 G_REGEX_OPTIMIZE, 0, NULL);
1021 get_var_value(gchar *name) {
1024 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1025 if(c->type == TYPE_STRING)
1026 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1027 else if(c->type == TYPE_INT)
1028 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1037 if(*uzbl.net.proxy_url == ' '
1038 || uzbl.net.proxy_url == NULL) {
1039 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1040 (GType) SOUP_SESSION_PROXY_URI);
1043 suri = soup_uri_new(uzbl.net.proxy_url);
1044 g_object_set(G_OBJECT(uzbl.net.soup_session),
1045 SOUP_SESSION_PROXY_URI,
1047 soup_uri_free(suri);
1054 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1055 g_array_append_val (a, uzbl.state.uri);
1056 load_uri(uzbl.gui.web_view, a);
1057 g_array_free (a, TRUE);
1061 cmd_always_insert_mode() {
1062 uzbl.behave.insert_mode =
1063 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1069 g_object_set(G_OBJECT(uzbl.net.soup_session),
1070 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1074 cmd_max_conns_host() {
1075 g_object_set(G_OBJECT(uzbl.net.soup_session),
1076 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1081 soup_session_remove_feature
1082 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1083 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1084 /*g_free(uzbl.net.soup_logger);*/
1086 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1087 soup_session_add_feature(uzbl.net.soup_session,
1088 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1092 cmd_default_font_size() {
1093 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1094 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1098 cmd_minimum_font_size() {
1099 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1100 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1107 buf = init_fifo(uzbl.behave.fifo_dir);
1108 if(uzbl.behave.fifo_dir)
1109 free(uzbl.behave.fifo_dir);
1111 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1118 buf = init_socket(uzbl.behave.fifo_dir);
1119 if(uzbl.behave.socket_dir)
1120 free(uzbl.behave.socket_dir);
1122 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1130 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1131 uzbl.behave.modmask = 0;
1133 if(uzbl.behave.modkey)
1134 free(uzbl.behave.modkey);
1136 for (i = 0; modkeys[i].key != NULL; i++) {
1137 if (g_strrstr(buf, modkeys[i].key))
1138 uzbl.behave.modmask |= modkeys[i].mask;
1146 buf = set_useragent(uzbl.net.useragent);
1147 if(uzbl.net.useragent)
1148 free(uzbl.net.useragent);
1150 uzbl.net.useragent = buf?buf:g_strdup("");
1155 gtk_widget_ref(uzbl.gui.scrolled_win);
1156 gtk_widget_ref(uzbl.gui.mainbar);
1157 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1158 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1160 if(uzbl.behave.status_top) {
1161 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1162 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1165 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1166 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1168 gtk_widget_unref(uzbl.gui.scrolled_win);
1169 gtk_widget_unref(uzbl.gui.mainbar);
1170 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1175 set_var_value(gchar *name, gchar *val) {
1177 uzbl_cmdprop *c = NULL;
1180 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1181 /* check for the variable type */
1182 if (c->type == TYPE_STRING) {
1184 *c->ptr = g_strdup(val);
1185 } else if(c->type == TYPE_INT) {
1187 *ip = (int)strtoul(val, &endp, 10);
1190 /* invoke a command specific function */
1191 if(c->func) c->func();
1193 /* this will be removed as soon as we have converted to
1194 * the callback interface
1202 runcmd(WebKitWebView* page, GArray *argv) {
1204 parse_cmd_line(argv_idx(argv, 0));
1208 parse_cmd_line(const char *ctl_line) {
1212 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1213 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1214 if(tokens[0][0] == 0) {
1215 gchar* value = parseenv (tokens[2]);
1216 set_var_value(tokens[1], value);
1221 printf("Error in command: %s\n", tokens[0]);
1224 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1225 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1226 if(tokens[0][0] == 0) {
1227 get_var_value(tokens[1]);
1231 printf("Error in command: %s\n", tokens[0]);
1234 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1235 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1236 if(tokens[0][0] == 0) {
1237 gchar* value = parseenv (tokens[2]);
1238 add_binding(tokens[1], value);
1243 printf("Error in command: %s\n", tokens[0]);
1246 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1247 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1248 if(tokens[0][0] == 0) {
1249 parse_command(tokens[1], tokens[2]);
1253 printf("Error in command: %s\n", tokens[0]);
1255 /* KEYCMD command */
1256 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1257 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1258 if(tokens[0][0] == 0) {
1259 /* should incremental commands want each individual "keystroke"
1260 sent in a loop or the whole string in one go like now? */
1261 g_string_assign(uzbl.state.keycmd, tokens[1]);
1263 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1270 else if( (ctl_line[0] == '#')
1271 || (ctl_line[0] == ' ')
1272 || (ctl_line[0] == '\n'))
1273 ; /* ignore these lines */
1275 printf("Command not understood (%s)\n", ctl_line);
1281 build_stream_name(int type, const gchar* dir) {
1283 State *s = &uzbl.state;
1286 xwin_str = itos((int)uzbl.xwin);
1288 str = g_strdup_printf
1289 ("%s/uzbl_fifo_%s", dir,
1290 s->instance_name ? s->instance_name : xwin_str);
1291 } else if (type == SOCKET) {
1292 str = g_strdup_printf
1293 ("%s/uzbl_socket_%s", dir,
1294 s->instance_name ? s->instance_name : xwin_str );
1301 control_fifo(GIOChannel *gio, GIOCondition condition) {
1302 if (uzbl.state.verbose)
1303 printf("triggered\n");
1308 if (condition & G_IO_HUP)
1309 g_error ("Fifo: Read end of pipe died!\n");
1312 g_error ("Fifo: GIOChannel broke\n");
1314 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1315 if (ret == G_IO_STATUS_ERROR) {
1316 g_error ("Fifo: Error reading: %s\n", err->message);
1320 parse_cmd_line(ctl_line);
1327 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1328 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1329 if (unlink(uzbl.comm.fifo_path) == -1)
1330 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1331 g_free(uzbl.comm.fifo_path);
1332 uzbl.comm.fifo_path = NULL;
1335 if (*dir == ' ') { /* space unsets the variable */
1340 GIOChannel *chan = NULL;
1341 GError *error = NULL;
1342 gchar *path = build_stream_name(FIFO, dir);
1344 if (!file_exists(path)) {
1345 if (mkfifo (path, 0666) == 0) {
1346 // 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.
1347 chan = g_io_channel_new_file(path, "r+", &error);
1349 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1350 if (uzbl.state.verbose)
1351 printf ("init_fifo: created successfully as %s\n", path);
1352 uzbl.comm.fifo_path = path;
1354 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1355 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1356 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1357 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1359 /* if we got this far, there was an error; cleanup */
1360 if (error) g_error_free (error);
1367 control_stdin(GIOChannel *gio, GIOCondition condition) {
1368 gchar *ctl_line = NULL;
1369 gsize ctl_line_len = 0;
1372 if (condition & G_IO_HUP) {
1373 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1377 ret = g_io_channel_read_line(gio, &ctl_line, &ctl_line_len, NULL, NULL);
1378 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1381 parse_cmd_line(ctl_line);
1389 GIOChannel *chan = NULL;
1390 GError *error = NULL;
1392 chan = g_io_channel_unix_new(fileno(stdin));
1394 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1395 g_error ("Stdin: could not add watch\n");
1397 if (uzbl.state.verbose)
1398 printf ("Stdin: watch added successfully\n");
1401 g_error ("Stdin: Error while opening: %s\n", error->message);
1403 if (error) g_error_free (error);
1407 control_socket(GIOChannel *chan) {
1408 struct sockaddr_un remote;
1409 char buffer[512], *ctl_line;
1411 int sock, clientsock, n, done;
1414 sock = g_io_channel_unix_get_fd(chan);
1416 memset (buffer, 0, sizeof (buffer));
1418 t = sizeof (remote);
1419 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1423 memset (temp, 0, sizeof (temp));
1424 n = recv (clientsock, temp, 128, 0);
1426 buffer[strlen (buffer)] = '\0';
1430 strcat (buffer, temp);
1433 if (strcmp (buffer, "\n") < 0) {
1434 buffer[strlen (buffer) - 1] = '\0';
1436 buffer[strlen (buffer)] = '\0';
1439 ctl_line = g_strdup(buffer);
1440 parse_cmd_line (ctl_line);
1443 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1444 GError *error = NULL;
1447 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1448 if (ret == G_IO_STATUS_ERROR)
1449 g_error ("Error reading: %s\n", error->message);
1451 printf("Got line %s (%u bytes) \n",ctl_line, len);
1453 parse_line(ctl_line);
1461 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1462 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1463 if (unlink(uzbl.comm.socket_path) == -1)
1464 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1465 g_free(uzbl.comm.socket_path);
1466 uzbl.comm.socket_path = NULL;
1474 GIOChannel *chan = NULL;
1476 struct sockaddr_un local;
1477 gchar *path = build_stream_name(SOCKET, dir);
1479 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1481 local.sun_family = AF_UNIX;
1482 strcpy (local.sun_path, path);
1483 unlink (local.sun_path);
1485 len = strlen (local.sun_path) + sizeof (local.sun_family);
1486 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1487 if (uzbl.state.verbose)
1488 printf ("init_socket: opened in %s\n", path);
1491 if( (chan = g_io_channel_unix_new(sock)) ) {
1492 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1493 uzbl.comm.socket_path = path;
1496 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1498 /* if we got this far, there was an error; cleanup */
1505 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1506 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1508 // this function may be called very early when the templates are not set (yet), hence the checks
1510 update_title (void) {
1511 Behaviour *b = &uzbl.behave;
1514 if (b->show_status) {
1515 if (b->title_format_short) {
1516 parsed = expand_template(b->title_format_short);
1517 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1520 if (b->status_format) {
1521 parsed = expand_template(b->status_format);
1522 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1525 if (b->status_background) {
1527 gdk_color_parse (b->status_background, &color);
1528 //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)
1529 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1532 if (b->title_format_long) {
1533 parsed = expand_template(b->title_format_long);
1534 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1541 key_press_cb (GtkWidget* window, GdkEventKey* event)
1543 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1547 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1548 || 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)
1551 /* turn off insert mode (if always_insert_mode is not used) */
1552 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1553 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1558 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1561 if (event->keyval == GDK_Escape) {
1562 g_string_truncate(uzbl.state.keycmd, 0);
1567 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1568 if (event->keyval == GDK_Insert) {
1570 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1571 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1573 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1576 g_string_append (uzbl.state.keycmd, str);
1583 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1584 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1588 gboolean key_ret = FALSE;
1589 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1591 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1593 run_keycmd(key_ret);
1595 if (key_ret) return (!uzbl.behave.insert_mode);
1600 run_keycmd(const gboolean key_ret) {
1601 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1603 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1604 g_string_truncate(uzbl.state.keycmd, 0);
1605 parse_command(action->name, action->param);
1609 /* try if it's an incremental keycmd or one that takes args, and run it */
1610 GString* short_keys = g_string_new ("");
1611 GString* short_keys_inc = g_string_new ("");
1613 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1614 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1615 g_string_assign(short_keys_inc, short_keys->str);
1616 g_string_append_c(short_keys, '_');
1617 g_string_append_c(short_keys_inc, '*');
1619 gboolean exec_now = FALSE;
1620 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1621 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1622 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1623 if (key_ret) { /* just quit the incremental command on return */
1624 g_string_truncate(uzbl.state.keycmd, 0);
1626 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1630 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1631 GString* actionname = g_string_new ("");
1632 GString* actionparam = g_string_new ("");
1633 g_string_erase (parampart, 0, i+1);
1635 g_string_printf (actionname, action->name, parampart->str);
1637 g_string_printf (actionparam, action->param, parampart->str);
1638 parse_command(actionname->str, actionparam->str);
1639 g_string_free (actionname, TRUE);
1640 g_string_free (actionparam, TRUE);
1641 g_string_free (parampart, TRUE);
1643 g_string_truncate(uzbl.state.keycmd, 0);
1647 g_string_truncate(short_keys, short_keys->len - 1);
1649 g_string_free (short_keys, TRUE);
1650 g_string_free (short_keys_inc, TRUE);
1657 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1658 //main_window_ref = g_object_ref(scrolled_window);
1659 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
1661 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1662 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1664 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1665 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1666 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1667 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1668 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1669 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1670 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1671 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1672 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1673 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1675 return scrolled_window;
1682 g->mainbar = gtk_hbox_new (FALSE, 0);
1684 /* keep a reference to the bar so we can re-pack it at runtime*/
1685 //sbar_ref = g_object_ref(g->mainbar);
1687 g->mainbar_label = gtk_label_new ("");
1688 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1689 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1690 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1691 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1692 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1697 GtkWidget* create_window () {
1698 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1699 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1700 gtk_widget_set_name (window, "Uzbl browser");
1701 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1702 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1708 run_handler (const gchar *act, const gchar *args) {
1709 char **parts = g_strsplit(act, " ", 2);
1711 else if ((g_strcmp0(parts[0], "spawn") == 0)
1712 || (g_strcmp0(parts[0], "sh") == 0)) {
1714 GString *a = g_string_new ("");
1716 spawnparts = split_quoted(parts[1], FALSE);
1717 g_string_append_printf(a, "%s", spawnparts[0]);
1718 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1719 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1720 g_string_append_printf(a, " %s", spawnparts[i]);
1721 parse_command(parts[0], a->str);
1722 g_string_free (a, TRUE);
1723 g_strfreev (spawnparts);
1725 parse_command(parts[0], parts[1]);
1730 add_binding (const gchar *key, const gchar *act) {
1731 char **parts = g_strsplit(act, " ", 2);
1738 if (uzbl.state.verbose)
1739 printf ("Binding %-10s : %s\n", key, act);
1740 action = new_action(parts[0], parts[1]);
1742 if(g_hash_table_lookup(uzbl.bindings, key))
1743 g_hash_table_remove(uzbl.bindings, key);
1744 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1750 get_xdg_var (XDG_Var xdg) {
1751 const gchar* actual_value = getenv (xdg.environmental);
1752 const gchar* home = getenv ("HOME");
1754 gchar* return_value = str_replace ("~", home, g_strdup (actual_value));
1756 if (! actual_value || strcmp (actual_value, "") == 0) {
1757 if (xdg.default_value) {
1758 return_value = str_replace ("~", home, g_strdup (xdg.default_value));
1760 return_value = NULL;
1764 return return_value;
1768 find_xdg_file (int xdg_type, char* filename) {
1769 /* xdg_type = 0 => config
1770 xdg_type = 1 => data
1771 xdg_type = 2 => cache*/
1773 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1774 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1777 gchar* temporary_string;
1780 if (! file_exists (temporary_file) && xdg_type != 2) {
1781 temporary_string = (char *) strtok_r (get_xdg_var (XDG[3 + xdg_type]), ":", &saveptr);
1783 while (temporary_string && ! file_exists (temporary_file)) {
1784 strcpy (temporary_file, temporary_string);
1785 strcat (temporary_file, filename);
1786 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1790 g_free (temporary_string);
1792 if (file_exists (temporary_file)) {
1793 return temporary_file;
1801 State *s = &uzbl.state;
1802 Network *n = &uzbl.net;
1804 uzbl.behave.reset_command_mode = 1;
1806 if (!s->config_file) {
1807 gchar* file = find_xdg_file (0, "/uzbl/config");
1808 s->config_file = g_strdup (file);
1812 if (s->config_file) {
1813 GIOChannel *chan = NULL;
1814 gchar *readbuf = NULL;
1817 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1820 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1821 == G_IO_STATUS_NORMAL) {
1822 parse_cmd_line(readbuf);
1826 g_io_channel_unref (chan);
1827 if (uzbl.state.verbose)
1828 printf ("Config %s loaded\n", s->config_file);
1830 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1833 if (uzbl.state.verbose)
1834 printf ("No configuration file loaded.\n");
1836 if (!uzbl.behave.status_format)
1837 set_var_value("status_format", STATUS_DEFAULT);
1838 if (!uzbl.behave.title_format_long)
1839 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1840 if (!uzbl.behave.title_format_short)
1841 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1844 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1848 set_useragent(gchar *val) {
1853 gchar *ua = expand_template(val);
1855 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1859 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1862 if (!uzbl.behave.cookie_handler) return;
1864 gchar * stdout = NULL;
1865 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1866 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1867 gchar *action = g_strdup ("GET");
1868 SoupURI * soup_uri = soup_message_get_uri(msg);
1869 sharg_append(a, action);
1870 sharg_append(a, soup_uri->host);
1871 sharg_append(a, soup_uri->path);
1872 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1873 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1875 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1878 g_array_free(a, TRUE);
1882 save_cookies (SoupMessage *msg, gpointer user_data){
1886 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1887 cookie = soup_cookie_to_set_cookie_header(ck->data);
1888 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1889 SoupURI * soup_uri = soup_message_get_uri(msg);
1890 gchar *action = strdup("PUT");
1891 sharg_append(a, action);
1892 sharg_append(a, soup_uri->host);
1893 sharg_append(a, soup_uri->path);
1894 sharg_append(a, cookie);
1895 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1898 g_array_free(a, TRUE);
1904 main (int argc, char* argv[]) {
1905 gtk_init (&argc, &argv);
1906 if (!g_thread_supported ())
1907 g_thread_init (NULL);
1909 uzbl.state.executable_path = g_strdup(argv[0]);
1910 uzbl.state.selected_url = NULL;
1911 uzbl.state.searchtx = NULL;
1913 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1914 g_option_context_add_main_entries (context, entries, NULL);
1915 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1916 g_option_context_parse (context, &argc, &argv, NULL);
1917 /* initialize hash table */
1918 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1920 uzbl.net.soup_session = webkit_get_default_session();
1921 uzbl.state.keycmd = g_string_new("");
1923 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1924 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1925 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1926 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1928 if(uname(&uzbl.state.unameinfo) == -1)
1929 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1934 make_var_to_name_hash();
1936 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1938 uzbl.gui.scrolled_win = create_browser();
1941 /* initial packing */
1942 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1943 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1945 uzbl.gui.main_window = create_window ();
1946 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1948 //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1950 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1951 gtk_widget_show_all (uzbl.gui.main_window);
1952 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1954 if (uzbl.state.verbose) {
1955 printf("Uzbl start location: %s\n", argv[0]);
1956 printf("window_id %i\n",(int) uzbl.xwin);
1957 printf("pid %i\n", getpid ());
1958 printf("name: %s\n", uzbl.state.instance_name);
1961 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1962 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1963 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1964 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1965 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1969 if (!uzbl.behave.show_status)
1970 gtk_widget_hide(uzbl.gui.mainbar);
1976 //if(uzbl.state.uri)
1977 // load_uri (uzbl.gui.web_view, uzbl.state.uri);
1982 return EXIT_SUCCESS;
1985 /* vi: set et ts=4: */