1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
65 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
67 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69 { NULL, 0, 0, 0, NULL, NULL, NULL }
73 /* associate command names to their properties */
74 typedef const struct {
80 enum {TYPE_INT, TYPE_STR};
82 /* some abbreviations to help keep the table lines' width humane */
83 #define VAR(x) .ptr = (void*)&(x)
84 #define T(x) .type = TYPE_##x
85 #define FUN(x) .func = x
90 } var_name_to_ptr[] = {
91 /* variable name pointer to variable in code var type callback function */
92 /* -------------------------------------------------------------------------------------------- */
93 { "uri", {VAR(uzbl.state.uri), T(STR), FUN(cmd_load_uri)}},
94 { "status_message", {VAR(uzbl.gui.sbar.msg), T(STR), FUN(update_title)}},
95 { "show_status", {VAR(uzbl.behave.show_status), T(INT), FUN(cmd_set_status)}},
96 { "status_top", {VAR(uzbl.behave.status_top), T(INT), FUN(move_statusbar)}},
97 { "status_format", {VAR(uzbl.behave.status_format), T(STR), FUN(update_title)}},
98 { "status_pbar_done", {VAR(uzbl.gui.sbar.progress_s), T(STR), FUN(update_title)}},
99 { "status_pbar_pending",{VAR(uzbl.gui.sbar.progress_u), T(STR), FUN(update_title)}},
100 { "status_pbar_width", {VAR(uzbl.gui.sbar.progress_w), T(INT), FUN(update_title)}},
101 { "status_background", {VAR(uzbl.behave.status_background), T(STR), FUN(update_title)}},
102 { "title_format_long", {VAR(uzbl.behave.title_format_long), T(STR), FUN(update_title)}},
103 { "title_format_short", {VAR(uzbl.behave.title_format_short), T(STR), FUN(update_title)}},
104 { "insert_mode", {VAR(uzbl.behave.insert_mode), T(INT), FUN(NULL)}},
105 { "always_insert_mode", {VAR(uzbl.behave.always_insert_mode), T(INT), FUN(cmd_always_insert_mode)}},
106 { "reset_command_mode", {VAR(uzbl.behave.reset_command_mode), T(INT), FUN(NULL)}},
107 { "modkey", {VAR(uzbl.behave.modkey), T(STR), FUN(cmd_modkey)}},
108 { "load_finish_handler",{VAR(uzbl.behave.load_finish_handler), T(STR), FUN(NULL)}},
109 { "load_start_handler", {VAR(uzbl.behave.load_start_handler), T(STR), FUN(NULL)}},
110 { "load_commit_handler",{VAR(uzbl.behave.load_commit_handler), T(STR), FUN(NULL)}},
111 { "history_handler", {VAR(uzbl.behave.history_handler), T(STR), FUN(NULL)}},
112 { "download_handler", {VAR(uzbl.behave.download_handler), T(STR), FUN(NULL)}},
113 { "cookie_handler", {VAR(uzbl.behave.cookie_handler), T(STR), FUN(NULL)}},
114 { "fifo_dir", {VAR(uzbl.behave.fifo_dir), T(STR), FUN(cmd_fifo_dir)}},
115 { "socket_dir", {VAR(uzbl.behave.socket_dir), T(STR), FUN(cmd_socket_dir)}},
116 { "http_debug", {VAR(uzbl.behave.http_debug), T(INT), FUN(cmd_http_debug)}},
117 { "font_size", {VAR(uzbl.behave.font_size), T(INT), FUN(cmd_font_size)}},
118 { "monospace_size", {VAR(uzbl.behave.monospace_size), T(INT), FUN(cmd_font_size)}},
119 { "minimum_font_size", {VAR(uzbl.behave.minimum_font_size), T(INT), FUN(cmd_minimum_font_size)}},
120 { "disable_plugins", {VAR(uzbl.behave.disable_plugins), T(INT), FUN(cmd_disable_plugins)}},
121 { "shell_cmd", {VAR(uzbl.behave.shell_cmd), T(STR), FUN(NULL)}},
122 { "proxy_url", {VAR(uzbl.net.proxy_url), T(STR), FUN(set_proxy_url)}},
123 { "max_conns", {VAR(uzbl.net.max_conns), T(INT), FUN(cmd_max_conns)}},
124 { "max_conns_host", {VAR(uzbl.net.max_conns_host), T(INT), FUN(cmd_max_conns_host)}},
125 { "useragent", {VAR(uzbl.net.useragent), T(STR), FUN(cmd_useragent)}},
126 { NULL, {.ptr = NULL, T(INT), FUN(NULL)}}
127 }, *n2v_p = var_name_to_ptr;
133 { "SHIFT", GDK_SHIFT_MASK }, // shift
134 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
135 { "CONTROL", GDK_CONTROL_MASK }, // control
136 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
137 { "MOD2", GDK_MOD2_MASK }, // 5th mod
138 { "MOD3", GDK_MOD3_MASK }, // 6th mod
139 { "MOD4", GDK_MOD4_MASK }, // 7th mod
140 { "MOD5", GDK_MOD5_MASK }, // 8th mod
141 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
142 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
143 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
144 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
145 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
146 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
147 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
148 { "META", GDK_META_MASK }, // meta (since 2.10)
153 /* construct a hash from the var_name_to_ptr array for quick access */
155 make_var_to_name_hash() {
156 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
158 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
164 /* --- UTILITY FUNCTIONS --- */
170 snprintf(tmp, sizeof(tmp), "%i", val);
171 return g_strdup(tmp);
175 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
178 str_replace (const char* search, const char* replace, const char* string) {
182 buf = g_strsplit (string, search, -1);
183 ret = g_strjoinv (replace, buf);
184 g_strfreev(buf); // somebody said this segfaults
190 read_file_by_line (gchar *path) {
191 GIOChannel *chan = NULL;
192 gchar *readbuf = NULL;
194 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
197 chan = g_io_channel_new_file(path, "r", NULL);
200 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
201 const gchar* val = g_strdup (readbuf);
202 g_array_append_val (lines, val);
207 g_io_channel_unref (chan);
209 fprintf(stderr, "File '%s' not be read.\n", path);
216 gchar* parseenv (char* string) {
217 extern char** environ;
218 gchar* tmpstr = NULL;
222 while (environ[i] != NULL) {
223 gchar** env = g_strsplit (environ[i], "=", 2);
224 gchar* envname = g_strconcat ("$", env[0], NULL);
226 if (g_strrstr (string, envname) != NULL) {
227 tmpstr = g_strdup(string);
229 string = str_replace(envname, env[1], tmpstr);
234 g_strfreev (env); // somebody said this breaks uzbl
242 setup_signal(int signr, sigfunc *shandler) {
243 struct sigaction nh, oh;
245 nh.sa_handler = shandler;
246 sigemptyset(&nh.sa_mask);
249 if(sigaction(signr, &nh, &oh) < 0)
257 if (uzbl.behave.fifo_dir)
258 unlink (uzbl.comm.fifo_path);
259 if (uzbl.behave.socket_dir)
260 unlink (uzbl.comm.socket_path);
262 g_free(uzbl.state.executable_path);
263 g_string_free(uzbl.state.keycmd, TRUE);
264 g_hash_table_destroy(uzbl.bindings);
265 g_hash_table_destroy(uzbl.behave.commands);
269 /* --- SIGNAL HANDLER --- */
272 catch_sigterm(int s) {
278 catch_sigint(int s) {
284 /* --- CALLBACKS --- */
287 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
290 (void) navigation_action;
291 (void) policy_decision;
293 const gchar* uri = webkit_network_request_get_uri (request);
294 if (uzbl.state.verbose)
295 printf("New window requested -> %s \n", uri);
296 new_window_load_uri(uri);
301 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
305 if (uzbl.state.selected_url != NULL) {
306 if (uzbl.state.verbose)
307 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
308 new_window_load_uri(uzbl.state.selected_url);
310 if (uzbl.state.verbose)
311 printf("New web view -> %s\n","Nothing to open, exiting");
317 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
320 if (uzbl.behave.download_handler) {
321 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
322 if (uzbl.state.verbose)
323 printf("Download -> %s\n",uri);
324 /* if urls not escaped, we may have to escape and quote uri before this call */
325 run_handler(uzbl.behave.download_handler, uri);
330 /* scroll a bar in a given direction */
332 scroll (GtkAdjustment* bar, GArray *argv) {
336 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
337 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
338 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
341 static void scroll_begin(WebKitWebView* page, GArray *argv) {
342 (void) page; (void) argv;
343 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
346 static void scroll_end(WebKitWebView* page, GArray *argv) {
347 (void) page; (void) argv;
348 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
349 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
352 static void scroll_vert(WebKitWebView* page, GArray *argv) {
354 scroll(uzbl.gui.bar_v, argv);
357 static void scroll_horz(WebKitWebView* page, GArray *argv) {
359 scroll(uzbl.gui.bar_h, argv);
364 if (!uzbl.behave.show_status) {
365 gtk_widget_hide(uzbl.gui.mainbar);
367 gtk_widget_show(uzbl.gui.mainbar);
373 toggle_status_cb (WebKitWebView* page, GArray *argv) {
377 if (uzbl.behave.show_status) {
378 gtk_widget_hide(uzbl.gui.mainbar);
380 gtk_widget_show(uzbl.gui.mainbar);
382 uzbl.behave.show_status = !uzbl.behave.show_status;
387 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
391 //Set selected_url state variable
392 g_free(uzbl.state.selected_url);
393 uzbl.state.selected_url = NULL;
395 uzbl.state.selected_url = g_strdup(link);
401 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
405 if (uzbl.gui.main_title)
406 g_free (uzbl.gui.main_title);
407 uzbl.gui.main_title = g_strdup (title);
412 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
415 uzbl.gui.sbar.load_progress = progress;
420 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
424 if (uzbl.behave.load_finish_handler)
425 run_handler(uzbl.behave.load_finish_handler, "");
429 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
433 if (uzbl.behave.load_start_handler)
434 run_handler(uzbl.behave.load_start_handler, "");
438 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
441 g_free (uzbl.state.uri);
442 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
443 uzbl.state.uri = g_string_free (newuri, FALSE);
444 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
445 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
448 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
449 if (uzbl.behave.load_commit_handler)
450 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
454 destroy_cb (GtkWidget* widget, gpointer data) {
462 if (uzbl.behave.history_handler) {
464 struct tm * timeinfo;
467 timeinfo = localtime ( &rawtime );
468 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
469 run_handler(uzbl.behave.history_handler, date);
474 /* VIEW funcs (little webkit wrappers) */
475 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
477 VIEWFUNC(reload_bypass_cache)
478 VIEWFUNC(stop_loading)
485 /* -- command to callback/function map for things we cannot attach to any signals */
487 static struct {char *name; Command command[2];} cmdlist[] =
488 { /* key function no_split */
489 { "back", {view_go_back, 0} },
490 { "forward", {view_go_forward, 0} },
491 { "scroll_vert", {scroll_vert, 0} },
492 { "scroll_horz", {scroll_horz, 0} },
493 { "scroll_begin", {scroll_begin, 0} },
494 { "scroll_end", {scroll_end, 0} },
495 { "reload", {view_reload, 0}, },
496 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
497 { "stop", {view_stop_loading, 0}, },
498 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
499 { "zoom_out", {view_zoom_out, 0}, },
500 { "uri", {load_uri, NOSPLIT} },
501 { "js", {run_js, NOSPLIT} },
502 { "script", {run_external_js, 0} },
503 { "toggle_status", {toggle_status_cb, 0} },
504 { "spawn", {spawn, 0} },
505 { "sh", {spawn_sh, 0} },
506 { "exit", {close_uzbl, 0} },
507 { "search", {search_forward_text, NOSPLIT} },
508 { "search_reverse", {search_reverse_text, NOSPLIT} },
509 { "toggle_insert_mode", {toggle_insert_mode, 0} },
510 { "runcmd", {runcmd, NOSPLIT} }
517 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
519 for (i = 0; i < LENGTH(cmdlist); i++)
520 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
523 /* -- CORE FUNCTIONS -- */
526 free_action(gpointer act) {
527 Action *action = (Action*)act;
528 g_free(action->name);
530 g_free(action->param);
535 new_action(const gchar *name, const gchar *param) {
536 Action *action = g_new(Action, 1);
538 action->name = g_strdup(name);
540 action->param = g_strdup(param);
542 action->param = NULL;
548 file_exists (const char * filename) {
549 return (access(filename, F_OK) == 0);
553 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
556 if (argv_idx(argv, 0)) {
557 if (strcmp (argv_idx(argv, 0), "0") == 0) {
558 uzbl.behave.insert_mode = FALSE;
560 uzbl.behave.insert_mode = TRUE;
563 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
570 load_uri (WebKitWebView *web_view, GArray *argv) {
571 if (argv_idx(argv, 0)) {
572 GString* newuri = g_string_new (argv_idx(argv, 0));
573 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
574 g_string_prepend (newuri, "http://");
575 /* if we do handle cookies, ask our handler for them */
576 webkit_web_view_load_uri (web_view, newuri->str);
577 g_string_free (newuri, TRUE);
582 run_js (WebKitWebView * web_view, GArray *argv) {
583 if (argv_idx(argv, 0))
584 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
588 run_external_js (WebKitWebView * web_view, GArray *argv) {
589 if (argv_idx(argv, 0)) {
590 GArray* lines = read_file_by_line (argv_idx (argv, 0));
595 while ((line = g_array_index(lines, gchar*, i))) {
597 js = g_strdup (line);
599 gchar* newjs = g_strconcat (js, line, NULL);
606 if (uzbl.state.verbose)
607 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
609 if (argv_idx (argv, 1)) {
610 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
614 webkit_web_view_execute_script (web_view, js);
616 g_array_free (lines, TRUE);
621 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
622 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
623 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
624 webkit_web_view_unmark_text_matches (page);
625 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
626 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
630 if (uzbl.state.searchtx) {
631 if (uzbl.state.verbose)
632 printf ("Searching: %s\n", uzbl.state.searchtx);
633 webkit_web_view_set_highlight_text_matches (page, TRUE);
634 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
639 search_forward_text (WebKitWebView *page, GArray *argv) {
640 search_text(page, argv, TRUE);
644 search_reverse_text (WebKitWebView *page, GArray *argv) {
645 search_text(page, argv, FALSE);
649 new_window_load_uri (const gchar * uri) {
650 GString* to_execute = g_string_new ("");
651 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
653 for (i = 0; entries[i].long_name != NULL; i++) {
654 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
655 gchar** str = (gchar**)entries[i].arg_data;
657 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
661 if (uzbl.state.verbose)
662 printf("\n%s\n", to_execute->str);
663 g_spawn_command_line_async (to_execute->str, NULL);
664 g_string_free (to_execute, TRUE);
668 close_uzbl (WebKitWebView *page, GArray *argv) {
674 /* --Statusbar functions-- */
676 build_progressbar_ascii(int percent) {
677 int width=uzbl.gui.sbar.progress_w;
680 GString *bar = g_string_new("");
682 l = (double)percent*((double)width/100.);
683 l = (int)(l+.5)>=(int)l ? l+.5 : l;
685 for(i=0; i<(int)l; i++)
686 g_string_append(bar, uzbl.gui.sbar.progress_s);
689 g_string_append(bar, uzbl.gui.sbar.progress_u);
691 return g_string_free(bar, FALSE);
696 const GScannerConfig scan_config = {
699 ) /* cset_skip_characters */,
704 ) /* cset_identifier_first */,
711 ) /* cset_identifier_nth */,
712 ( "" ) /* cpair_comment_single */,
714 TRUE /* case_sensitive */,
716 FALSE /* skip_comment_multi */,
717 FALSE /* skip_comment_single */,
718 FALSE /* scan_comment_multi */,
719 TRUE /* scan_identifier */,
720 TRUE /* scan_identifier_1char */,
721 FALSE /* scan_identifier_NULL */,
722 TRUE /* scan_symbols */,
723 FALSE /* scan_binary */,
724 FALSE /* scan_octal */,
725 FALSE /* scan_float */,
726 FALSE /* scan_hex */,
727 FALSE /* scan_hex_dollar */,
728 FALSE /* scan_string_sq */,
729 FALSE /* scan_string_dq */,
730 TRUE /* numbers_2_int */,
731 FALSE /* int_2_float */,
732 FALSE /* identifier_2_string */,
733 FALSE /* char_2_token */,
734 FALSE /* symbol_2_token */,
735 TRUE /* scope_0_fallback */,
740 uzbl.scan = g_scanner_new(&scan_config);
741 while(symp->symbol_name) {
742 g_scanner_scope_add_symbol(uzbl.scan, 0,
744 GINT_TO_POINTER(symp->symbol_token));
750 expand_template(const char *template) {
751 if(!template) return NULL;
753 GTokenType token = G_TOKEN_NONE;
754 GString *ret = g_string_new("");
758 g_scanner_input_text(uzbl.scan, template, strlen(template));
759 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
760 token = g_scanner_get_next_token(uzbl.scan);
762 if(token == G_TOKEN_SYMBOL) {
763 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
766 buf = uzbl.state.uri?
767 g_markup_printf_escaped("%s", uzbl.state.uri) :
769 g_string_append(ret, buf);
773 buf = itos(uzbl.gui.sbar.load_progress);
774 g_string_append(ret, buf);
777 case SYM_LOADPRGSBAR:
778 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
779 g_string_append(ret, buf);
783 buf = uzbl.gui.main_title?
784 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
786 g_string_append(ret, buf);
789 case SYM_SELECTED_URI:
790 buf = uzbl.state.selected_url?
791 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
793 g_string_append(ret, buf);
797 buf = itos(uzbl.xwin);
799 uzbl.state.instance_name?uzbl.state.instance_name:buf);
803 buf = uzbl.state.keycmd->str?
804 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
806 g_string_append(ret, buf);
811 uzbl.behave.insert_mode?"[I]":"[C]");
815 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
819 buf = itos(WEBKIT_MAJOR_VERSION);
820 g_string_append(ret, buf);
824 buf = itos(WEBKIT_MINOR_VERSION);
825 g_string_append(ret, buf);
829 buf = itos(WEBKIT_MICRO_VERSION);
830 g_string_append(ret, buf);
834 g_string_append(ret, uzbl.state.unameinfo.sysname);
837 g_string_append(ret, uzbl.state.unameinfo.nodename);
840 g_string_append(ret, uzbl.state.unameinfo.release);
843 g_string_append(ret, uzbl.state.unameinfo.version);
846 g_string_append(ret, uzbl.state.unameinfo.machine);
849 g_string_append(ret, ARCH);
853 g_string_append(ret, uzbl.state.unameinfo.domainname);
857 g_string_append(ret, COMMIT);
863 else if(token == G_TOKEN_INT) {
864 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
865 g_string_append(ret, buf);
868 else if(token == G_TOKEN_IDENTIFIER) {
869 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
871 else if(token == G_TOKEN_CHAR) {
872 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
876 return g_string_free(ret, FALSE);
878 /* --End Statusbar functions-- */
881 sharg_append(GArray *a, const gchar *str) {
882 const gchar *s = (str ? str : "");
883 g_array_append_val(a, s);
886 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
888 run_command (const gchar *command, const guint npre, const gchar **args,
889 const gboolean sync, char **stdout) {
890 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
893 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
894 gchar *pid = itos(getpid());
895 gchar *xwin = itos(uzbl.xwin);
897 sharg_append(a, command);
898 for (i = 0; i < npre; i++) /* add n args before the default vars */
899 sharg_append(a, args[i]);
900 sharg_append(a, uzbl.state.config_file);
901 sharg_append(a, pid);
902 sharg_append(a, xwin);
903 sharg_append(a, uzbl.comm.fifo_path);
904 sharg_append(a, uzbl.comm.socket_path);
905 sharg_append(a, uzbl.state.uri);
906 sharg_append(a, uzbl.gui.main_title);
908 for (i = npre; i < g_strv_length((gchar**)args); i++)
909 sharg_append(a, args[i]);
912 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
913 NULL, NULL, stdout, NULL, NULL, &err);
914 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
915 NULL, NULL, NULL, &err);
917 if (uzbl.state.verbose) {
918 GString *s = g_string_new("spawned:");
919 for (i = 0; i < (a->len); i++) {
920 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
921 g_string_append_printf(s, " %s", qarg);
924 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
925 printf("%s\n", s->str);
926 g_string_free(s, TRUE);
929 g_printerr("error on run_command: %s\n", err->message);
934 g_array_free (a, TRUE);
939 split_quoted(const gchar* src, const gboolean unquote) {
940 /* split on unquoted space, return array of strings;
941 remove a layer of quotes and backslashes if unquote */
942 if (!src) return NULL;
946 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
947 GString *s = g_string_new ("");
951 for (p = src; *p != '\0'; p++) {
952 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
953 else if (*p == '\\') { g_string_append_c(s, *p++);
954 g_string_append_c(s, *p); }
955 else if ((*p == '"') && unquote && !sq) dq = !dq;
956 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
958 else if ((*p == '\'') && unquote && !dq) sq = !sq;
959 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
961 else if ((*p == ' ') && !dq && !sq) {
962 dup = g_strdup(s->str);
963 g_array_append_val(a, dup);
964 g_string_truncate(s, 0);
965 } else g_string_append_c(s, *p);
967 dup = g_strdup(s->str);
968 g_array_append_val(a, dup);
969 ret = (gchar**)a->data;
970 g_array_free (a, FALSE);
971 g_string_free (s, TRUE);
976 spawn(WebKitWebView *web_view, GArray *argv) {
978 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
979 if (argv_idx(argv, 0))
980 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
984 spawn_sh(WebKitWebView *web_view, GArray *argv) {
986 if (!uzbl.behave.shell_cmd) {
987 g_printerr ("spawn_sh: shell_cmd is not set!\n");
992 gchar *spacer = g_strdup("");
993 g_array_insert_val(argv, 1, spacer);
994 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
996 for (i = 1; i < g_strv_length(cmd); i++)
997 g_array_prepend_val(argv, cmd[i]);
999 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1005 parse_command(const char *cmd, const char *param) {
1008 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1011 gchar **par = split_quoted(param, TRUE);
1012 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1014 if (c[1] == NOSPLIT) { /* don't split */
1015 sharg_append(a, param);
1017 for (i = 0; i < g_strv_length(par); i++)
1018 sharg_append(a, par[i]);
1020 c[0](uzbl.gui.web_view, a);
1022 g_array_free (a, TRUE);
1025 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1028 /* command parser */
1031 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1032 G_REGEX_OPTIMIZE, 0, NULL);
1033 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1034 G_REGEX_OPTIMIZE, 0, NULL);
1035 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1036 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1037 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1038 G_REGEX_OPTIMIZE, 0, NULL);
1039 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1040 G_REGEX_OPTIMIZE, 0, NULL);
1044 get_var_value(gchar *name) {
1047 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1048 if(c->type == TYPE_STR)
1049 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1050 else if(c->type == TYPE_INT)
1051 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1060 if(*uzbl.net.proxy_url == ' '
1061 || uzbl.net.proxy_url == NULL) {
1062 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1063 (GType) SOUP_SESSION_PROXY_URI);
1066 suri = soup_uri_new(uzbl.net.proxy_url);
1067 g_object_set(G_OBJECT(uzbl.net.soup_session),
1068 SOUP_SESSION_PROXY_URI,
1070 soup_uri_free(suri);
1077 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1078 g_array_append_val (a, uzbl.state.uri);
1079 load_uri(uzbl.gui.web_view, a);
1080 g_array_free (a, TRUE);
1084 cmd_always_insert_mode() {
1085 uzbl.behave.insert_mode =
1086 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1092 g_object_set(G_OBJECT(uzbl.net.soup_session),
1093 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1097 cmd_max_conns_host() {
1098 g_object_set(G_OBJECT(uzbl.net.soup_session),
1099 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1104 soup_session_remove_feature
1105 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1106 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1107 /*g_free(uzbl.net.soup_logger);*/
1109 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1110 soup_session_add_feature(uzbl.net.soup_session,
1111 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1116 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1117 if (uzbl.behave.font_size > 0) {
1118 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1121 if (uzbl.behave.monospace_size > 0) {
1122 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1123 uzbl.behave.monospace_size, NULL);
1125 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1126 uzbl.behave.font_size, NULL);
1131 cmd_disable_plugins() {
1132 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1133 g_object_set (G_OBJECT(ws), "enable-plugins", !uzbl.behave.disable_plugins, NULL);
1137 cmd_minimum_font_size() {
1138 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1139 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1144 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1149 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1157 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1158 uzbl.behave.modmask = 0;
1160 if(uzbl.behave.modkey)
1161 g_free(uzbl.behave.modkey);
1162 uzbl.behave.modkey = buf;
1164 for (i = 0; modkeys[i].key != NULL; i++) {
1165 if (g_strrstr(buf, modkeys[i].key))
1166 uzbl.behave.modmask |= modkeys[i].mask;
1172 if (*uzbl.net.useragent == ' ') {
1173 g_free (uzbl.net.useragent);
1174 uzbl.net.useragent = NULL;
1176 gchar *ua = expand_template(uzbl.net.useragent);
1178 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1179 g_free(uzbl.net.useragent);
1180 uzbl.net.useragent = ua;
1186 gtk_widget_ref(uzbl.gui.scrolled_win);
1187 gtk_widget_ref(uzbl.gui.mainbar);
1188 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1189 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1191 if(uzbl.behave.status_top) {
1192 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1193 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1196 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1197 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1199 gtk_widget_unref(uzbl.gui.scrolled_win);
1200 gtk_widget_unref(uzbl.gui.mainbar);
1201 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1206 set_var_value(gchar *name, gchar *val) {
1207 uzbl_cmdprop *c = NULL;
1210 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1211 /* check for the variable type */
1212 if (c->type == TYPE_STR) {
1214 *c->ptr = g_strdup(val);
1215 } else if(c->type == TYPE_INT) {
1216 int *ip = GPOINTER_TO_INT(c->ptr);
1217 *ip = (int)strtoul(val, &endp, 10);
1220 /* invoke a command specific function */
1221 if(c->func) c->func();
1227 runcmd(WebKitWebView* page, GArray *argv) {
1229 parse_cmd_line(argv_idx(argv, 0));
1233 parse_cmd_line(const char *ctl_line) {
1237 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1238 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1239 if(tokens[0][0] == 0) {
1240 gchar* value = parseenv(g_strdup(tokens[2]));
1241 set_var_value(tokens[1], value);
1246 printf("Error in command: %s\n", tokens[0]);
1249 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1250 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1251 if(tokens[0][0] == 0) {
1252 get_var_value(tokens[1]);
1256 printf("Error in command: %s\n", tokens[0]);
1259 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1260 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1261 if(tokens[0][0] == 0) {
1262 gchar* value = parseenv(g_strdup(tokens[2]));
1263 add_binding(tokens[1], value);
1268 printf("Error in command: %s\n", tokens[0]);
1271 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1272 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1273 if(tokens[0][0] == 0) {
1274 parse_command(tokens[1], tokens[2]);
1278 printf("Error in command: %s\n", tokens[0]);
1280 /* KEYCMD command */
1281 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1282 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1283 if(tokens[0][0] == 0) {
1284 /* should incremental commands want each individual "keystroke"
1285 sent in a loop or the whole string in one go like now? */
1286 g_string_assign(uzbl.state.keycmd, tokens[1]);
1288 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1295 else if( (ctl_line[0] == '#')
1296 || (ctl_line[0] == ' ')
1297 || (ctl_line[0] == '\n'))
1298 ; /* ignore these lines */
1300 printf("Command not understood (%s)\n", ctl_line);
1306 build_stream_name(int type, const gchar* dir) {
1308 State *s = &uzbl.state;
1311 xwin_str = itos((int)uzbl.xwin);
1313 str = g_strdup_printf
1314 ("%s/uzbl_fifo_%s", dir,
1315 s->instance_name ? s->instance_name : xwin_str);
1316 } else if (type == SOCKET) {
1317 str = g_strdup_printf
1318 ("%s/uzbl_socket_%s", dir,
1319 s->instance_name ? s->instance_name : xwin_str );
1326 control_fifo(GIOChannel *gio, GIOCondition condition) {
1327 if (uzbl.state.verbose)
1328 printf("triggered\n");
1333 if (condition & G_IO_HUP)
1334 g_error ("Fifo: Read end of pipe died!\n");
1337 g_error ("Fifo: GIOChannel broke\n");
1339 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1340 if (ret == G_IO_STATUS_ERROR) {
1341 g_error ("Fifo: Error reading: %s\n", err->message);
1345 parse_cmd_line(ctl_line);
1352 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1353 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1354 if (unlink(uzbl.comm.fifo_path) == -1)
1355 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1356 g_free(uzbl.comm.fifo_path);
1357 uzbl.comm.fifo_path = NULL;
1360 if (*dir == ' ') { /* space unsets the variable */
1365 GIOChannel *chan = NULL;
1366 GError *error = NULL;
1367 gchar *path = build_stream_name(FIFO, dir);
1369 if (!file_exists(path)) {
1370 if (mkfifo (path, 0666) == 0) {
1371 // 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.
1372 chan = g_io_channel_new_file(path, "r+", &error);
1374 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1375 if (uzbl.state.verbose)
1376 printf ("init_fifo: created successfully as %s\n", path);
1377 uzbl.comm.fifo_path = path;
1379 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1380 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1381 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1382 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1384 /* if we got this far, there was an error; cleanup */
1385 if (error) g_error_free (error);
1392 control_stdin(GIOChannel *gio, GIOCondition condition) {
1394 gchar *ctl_line = NULL;
1397 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1398 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1401 parse_cmd_line(ctl_line);
1409 GIOChannel *chan = NULL;
1410 GError *error = NULL;
1412 chan = g_io_channel_unix_new(fileno(stdin));
1414 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1415 g_error ("Stdin: could not add watch\n");
1417 if (uzbl.state.verbose)
1418 printf ("Stdin: watch added successfully\n");
1421 g_error ("Stdin: Error while opening: %s\n", error->message);
1423 if (error) g_error_free (error);
1427 control_socket(GIOChannel *chan) {
1428 struct sockaddr_un remote;
1429 char buffer[512], *ctl_line;
1431 int sock, clientsock, n, done;
1434 sock = g_io_channel_unix_get_fd(chan);
1436 memset (buffer, 0, sizeof (buffer));
1438 t = sizeof (remote);
1439 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1443 memset (temp, 0, sizeof (temp));
1444 n = recv (clientsock, temp, 128, 0);
1446 buffer[strlen (buffer)] = '\0';
1450 strcat (buffer, temp);
1453 if (strcmp (buffer, "\n") < 0) {
1454 buffer[strlen (buffer) - 1] = '\0';
1456 buffer[strlen (buffer)] = '\0';
1459 ctl_line = g_strdup(buffer);
1460 parse_cmd_line (ctl_line);
1463 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1464 GError *error = NULL;
1467 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1468 if (ret == G_IO_STATUS_ERROR)
1469 g_error ("Error reading: %s\n", error->message);
1471 printf("Got line %s (%u bytes) \n",ctl_line, len);
1473 parse_line(ctl_line);
1481 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1482 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1483 if (unlink(uzbl.comm.socket_path) == -1)
1484 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1485 g_free(uzbl.comm.socket_path);
1486 uzbl.comm.socket_path = NULL;
1494 GIOChannel *chan = NULL;
1496 struct sockaddr_un local;
1497 gchar *path = build_stream_name(SOCKET, dir);
1499 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1501 local.sun_family = AF_UNIX;
1502 strcpy (local.sun_path, path);
1503 unlink (local.sun_path);
1505 len = strlen (local.sun_path) + sizeof (local.sun_family);
1506 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1507 if (uzbl.state.verbose)
1508 printf ("init_socket: opened in %s\n", path);
1511 if( (chan = g_io_channel_unix_new(sock)) ) {
1512 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1513 uzbl.comm.socket_path = path;
1516 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1518 /* if we got this far, there was an error; cleanup */
1525 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1526 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1528 // this function may be called very early when the templates are not set (yet), hence the checks
1530 update_title (void) {
1531 Behaviour *b = &uzbl.behave;
1534 if (b->show_status) {
1535 if (b->title_format_short) {
1536 parsed = expand_template(b->title_format_short);
1537 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1540 if (b->status_format) {
1541 parsed = expand_template(b->status_format);
1542 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1545 if (b->status_background) {
1547 gdk_color_parse (b->status_background, &color);
1548 //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)
1549 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1552 if (b->title_format_long) {
1553 parsed = expand_template(b->title_format_long);
1554 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1561 key_press_cb (GtkWidget* window, GdkEventKey* event)
1563 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1567 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1568 || 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)
1571 /* turn off insert mode (if always_insert_mode is not used) */
1572 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1573 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1578 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1581 if (event->keyval == GDK_Escape) {
1582 g_string_truncate(uzbl.state.keycmd, 0);
1587 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1588 if (event->keyval == GDK_Insert) {
1590 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1591 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1593 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1596 g_string_append (uzbl.state.keycmd, str);
1603 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1604 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1608 gboolean key_ret = FALSE;
1609 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1611 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1613 run_keycmd(key_ret);
1615 if (key_ret) return (!uzbl.behave.insert_mode);
1620 run_keycmd(const gboolean key_ret) {
1621 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1623 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1624 g_string_truncate(uzbl.state.keycmd, 0);
1625 parse_command(action->name, action->param);
1629 /* try if it's an incremental keycmd or one that takes args, and run it */
1630 GString* short_keys = g_string_new ("");
1631 GString* short_keys_inc = g_string_new ("");
1633 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1634 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1635 g_string_assign(short_keys_inc, short_keys->str);
1636 g_string_append_c(short_keys, '_');
1637 g_string_append_c(short_keys_inc, '*');
1639 gboolean exec_now = FALSE;
1640 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1641 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1642 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1643 if (key_ret) { /* just quit the incremental command on return */
1644 g_string_truncate(uzbl.state.keycmd, 0);
1646 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1650 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1651 GString* actionname = g_string_new ("");
1652 GString* actionparam = g_string_new ("");
1653 g_string_erase (parampart, 0, i+1);
1655 g_string_printf (actionname, action->name, parampart->str);
1657 g_string_printf (actionparam, action->param, parampart->str);
1658 parse_command(actionname->str, actionparam->str);
1659 g_string_free (actionname, TRUE);
1660 g_string_free (actionparam, TRUE);
1661 g_string_free (parampart, TRUE);
1663 g_string_truncate(uzbl.state.keycmd, 0);
1667 g_string_truncate(short_keys, short_keys->len - 1);
1669 g_string_free (short_keys, TRUE);
1670 g_string_free (short_keys_inc, TRUE);
1677 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1678 //main_window_ref = g_object_ref(scrolled_window);
1679 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
1681 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1682 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1684 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1685 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1686 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1687 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1688 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1689 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1690 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1691 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1692 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1693 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1695 return scrolled_window;
1702 g->mainbar = gtk_hbox_new (FALSE, 0);
1704 /* keep a reference to the bar so we can re-pack it at runtime*/
1705 //sbar_ref = g_object_ref(g->mainbar);
1707 g->mainbar_label = gtk_label_new ("");
1708 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1709 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1710 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1711 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1712 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1717 GtkWidget* create_window () {
1718 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1719 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1720 gtk_widget_set_name (window, "Uzbl browser");
1721 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1722 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1728 run_handler (const gchar *act, const gchar *args) {
1729 char **parts = g_strsplit(act, " ", 2);
1731 else if ((g_strcmp0(parts[0], "spawn") == 0)
1732 || (g_strcmp0(parts[0], "sh") == 0)) {
1734 GString *a = g_string_new ("");
1736 spawnparts = split_quoted(parts[1], FALSE);
1737 g_string_append_printf(a, "%s", spawnparts[0]);
1738 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1739 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1740 g_string_append_printf(a, " %s", spawnparts[i]);
1741 parse_command(parts[0], a->str);
1742 g_string_free (a, TRUE);
1743 g_strfreev (spawnparts);
1745 parse_command(parts[0], parts[1]);
1750 add_binding (const gchar *key, const gchar *act) {
1751 char **parts = g_strsplit(act, " ", 2);
1758 if (uzbl.state.verbose)
1759 printf ("Binding %-10s : %s\n", key, act);
1760 action = new_action(parts[0], parts[1]);
1762 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1767 get_xdg_var (XDG_Var xdg) {
1768 const gchar* actual_value = getenv (xdg.environmental);
1769 const gchar* home = getenv ("HOME");
1771 gchar* return_value = str_replace ("~", home, actual_value);
1773 if (! actual_value || strcmp (actual_value, "") == 0) {
1774 if (xdg.default_value) {
1775 return_value = str_replace ("~", home, xdg.default_value);
1777 return_value = NULL;
1780 return return_value;
1784 find_xdg_file (int xdg_type, char* filename) {
1785 /* xdg_type = 0 => config
1786 xdg_type = 1 => data
1787 xdg_type = 2 => cache*/
1789 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1790 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1793 gchar* temporary_string;
1797 if (! file_exists (temporary_file) && xdg_type != 2) {
1798 buf = get_xdg_var (XDG[3 + xdg_type]);
1799 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1802 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1803 g_free (temporary_file);
1804 temporary_file = g_strconcat (temporary_string, filename, NULL);
1808 //g_free (temporary_string); - segfaults.
1810 if (file_exists (temporary_file)) {
1811 return temporary_file;
1818 State *s = &uzbl.state;
1819 Network *n = &uzbl.net;
1821 for (i = 0; default_config[i].command != NULL; i++) {
1822 parse_cmd_line(default_config[i].command);
1825 if (!s->config_file) {
1826 s->config_file = find_xdg_file (0, "/uzbl/config");
1829 if (s->config_file) {
1830 GArray* lines = read_file_by_line (s->config_file);
1834 while ((line = g_array_index(lines, gchar*, i))) {
1835 parse_cmd_line (line);
1839 g_array_free (lines, TRUE);
1841 if (uzbl.state.verbose)
1842 printf ("No configuration file loaded.\n");
1845 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1848 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1851 if (!uzbl.behave.cookie_handler) return;
1853 gchar * stdout = NULL;
1854 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1855 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1856 gchar *action = g_strdup ("GET");
1857 SoupURI * soup_uri = soup_message_get_uri(msg);
1858 sharg_append(a, action);
1859 sharg_append(a, soup_uri->host);
1860 sharg_append(a, soup_uri->path);
1861 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1862 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1864 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1867 g_array_free(a, TRUE);
1871 save_cookies (SoupMessage *msg, gpointer user_data){
1875 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1876 cookie = soup_cookie_to_set_cookie_header(ck->data);
1877 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1878 SoupURI * soup_uri = soup_message_get_uri(msg);
1879 gchar *action = strdup("PUT");
1880 sharg_append(a, action);
1881 sharg_append(a, soup_uri->host);
1882 sharg_append(a, soup_uri->path);
1883 sharg_append(a, cookie);
1884 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1887 g_array_free(a, TRUE);
1893 main (int argc, char* argv[]) {
1894 gtk_init (&argc, &argv);
1895 if (!g_thread_supported ())
1896 g_thread_init (NULL);
1897 uzbl.state.executable_path = g_strdup(argv[0]);
1898 uzbl.state.selected_url = NULL;
1899 uzbl.state.searchtx = NULL;
1901 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1902 g_option_context_add_main_entries (context, entries, NULL);
1903 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1904 g_option_context_parse (context, &argc, &argv, NULL);
1905 g_option_context_free(context);
1906 /* initialize hash table */
1907 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1909 uzbl.net.soup_session = webkit_get_default_session();
1910 uzbl.state.keycmd = g_string_new("");
1912 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1913 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1914 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1915 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1917 if(uname(&uzbl.state.unameinfo) == -1)
1918 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1920 uzbl.gui.sbar.progress_s = g_strdup("=");
1921 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1922 uzbl.gui.sbar.progress_w = 10;
1927 make_var_to_name_hash();
1929 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1931 uzbl.gui.scrolled_win = create_browser();
1934 /* initial packing */
1935 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1936 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1938 uzbl.gui.main_window = create_window ();
1939 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1942 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1943 gtk_widget_show_all (uzbl.gui.main_window);
1944 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1946 if (uzbl.state.verbose) {
1947 printf("Uzbl start location: %s\n", argv[0]);
1948 printf("window_id %i\n",(int) uzbl.xwin);
1949 printf("pid %i\n", getpid ());
1950 printf("name: %s\n", uzbl.state.instance_name);
1953 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1954 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1955 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1956 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1957 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1961 if (!uzbl.behave.show_status)
1962 gtk_widget_hide(uzbl.gui.mainbar);
1968 if(uzbl.state.uri) {
1969 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1970 g_array_append_val(a, uzbl.state.uri);
1971 load_uri (uzbl.gui.web_view, a);
1972 g_array_free (a, TRUE);
1978 return EXIT_SUCCESS;
1981 /* vi: set et ts=4: */