1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
61 /* define names and pointers to all config specific variables */
62 typedef const struct {
68 enum {TYPE_INT, TYPE_STRING};
73 } var_name_to_ptr[] = {
74 /* variable name pointer to variable in code variable type callback function */
75 /* ------------------------------------------------------------------------------------------------------------------- */
76 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
77 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
78 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
79 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
80 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
81 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
82 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
83 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
84 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
85 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
86 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
87 { "modkey" , {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
88 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
89 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
90 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
91 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
92 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
93 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
94 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
95 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
96 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
97 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
98 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
99 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
100 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
101 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
102 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
103 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
104 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
105 }, *n2v_p = var_name_to_ptr;
111 { "SHIFT", GDK_SHIFT_MASK }, // shift
112 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
113 { "CONTROL", GDK_CONTROL_MASK }, // control
114 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
115 { "MOD2", GDK_MOD2_MASK }, // 5th mod
116 { "MOD3", GDK_MOD3_MASK }, // 6th mod
117 { "MOD4", GDK_MOD4_MASK }, // 7th mod
118 { "MOD5", GDK_MOD5_MASK }, // 8th mod
119 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
120 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
121 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
122 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
123 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
124 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
125 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
126 { "META", GDK_META_MASK }, // meta (since 2.10)
131 /* construct a hash from the var_name_to_ptr array for quick access */
133 make_var_to_name_hash() {
134 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
136 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
141 /* commandline arguments (set initial values for the state variables) */
142 static GOptionEntry entries[] =
144 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
145 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
146 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
147 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
148 { NULL, 0, 0, 0, NULL, NULL, NULL }
151 typedef void (*Command)(WebKitWebView*, GArray *argv);
153 /* --- UTILITY FUNCTIONS --- */
159 snprintf(tmp, sizeof(tmp), "%i", val);
160 return g_strdup(tmp);
164 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
167 str_replace (const char* search, const char* replace, const char* string) {
171 buf = g_strsplit (string, search, -1);
172 ret = g_strjoinv (replace, buf);
179 read_file_by_line (gchar *path) {
180 GIOChannel *chan = NULL;
181 gchar *readbuf = NULL;
183 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
186 chan = g_io_channel_new_file(path, "r", NULL);
189 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
190 const gchar* val = g_strdup (readbuf);
191 g_array_append_val (lines, val);
196 g_io_channel_unref (chan);
198 fprintf(stderr, "File '%s' not be read.\n", path);
205 gchar* parseenv (const char* string) {
206 extern char** environ;
207 gchar* newstring = g_strdup (string);
210 while (environ[i] != NULL) {
211 gchar** env = g_strsplit (environ[i], "=", 0);
212 gchar* envname = malloc (strlen (env[0]) + 1);
214 strcat (envname, "$");
215 strcat (envname, env[0]);
217 newstring = str_replace(envname, env[1], newstring);
220 //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
228 setup_signal(int signr, sigfunc *shandler) {
229 struct sigaction nh, oh;
231 nh.sa_handler = shandler;
232 sigemptyset(&nh.sa_mask);
235 if(sigaction(signr, &nh, &oh) < 0)
243 if (uzbl.behave.fifo_dir)
244 unlink (uzbl.comm.fifo_path);
245 if (uzbl.behave.socket_dir)
246 unlink (uzbl.comm.socket_path);
248 g_free(uzbl.state.executable_path);
249 g_string_free(uzbl.state.keycmd, TRUE);
250 g_hash_table_destroy(uzbl.bindings);
251 g_hash_table_destroy(uzbl.behave.commands);
255 /* --- SIGNAL HANDLER --- */
258 catch_sigterm(int s) {
264 catch_sigint(int s) {
270 /* --- CALLBACKS --- */
273 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
276 (void) navigation_action;
277 (void) policy_decision;
279 const gchar* uri = webkit_network_request_get_uri (request);
280 if (uzbl.state.verbose)
281 printf("New window requested -> %s \n", uri);
282 new_window_load_uri(uri);
287 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
291 if (uzbl.state.selected_url != NULL) {
292 if (uzbl.state.verbose)
293 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
294 new_window_load_uri(uzbl.state.selected_url);
296 if (uzbl.state.verbose)
297 printf("New web view -> %s\n","Nothing to open, exiting");
303 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
306 if (uzbl.behave.download_handler) {
307 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
308 if (uzbl.state.verbose)
309 printf("Download -> %s\n",uri);
310 /* if urls not escaped, we may have to escape and quote uri before this call */
311 run_handler(uzbl.behave.download_handler, uri);
316 /* scroll a bar in a given direction */
318 scroll (GtkAdjustment* bar, GArray *argv) {
322 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
323 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
324 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
327 static void scroll_begin(WebKitWebView* page, GArray *argv) {
328 (void) page; (void) argv;
329 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
332 static void scroll_end(WebKitWebView* page, GArray *argv) {
333 (void) page; (void) argv;
334 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
335 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
338 static void scroll_vert(WebKitWebView* page, GArray *argv) {
340 scroll(uzbl.gui.bar_v, argv);
343 static void scroll_horz(WebKitWebView* page, GArray *argv) {
345 scroll(uzbl.gui.bar_h, argv);
350 if (!uzbl.behave.show_status) {
351 gtk_widget_hide(uzbl.gui.mainbar);
353 gtk_widget_show(uzbl.gui.mainbar);
359 toggle_status_cb (WebKitWebView* page, GArray *argv) {
363 if (uzbl.behave.show_status) {
364 gtk_widget_hide(uzbl.gui.mainbar);
366 gtk_widget_show(uzbl.gui.mainbar);
368 uzbl.behave.show_status = !uzbl.behave.show_status;
373 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
377 //Set selected_url state variable
378 g_free(uzbl.state.selected_url);
379 uzbl.state.selected_url = NULL;
381 uzbl.state.selected_url = g_strdup(link);
387 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
391 if (uzbl.gui.main_title)
392 g_free (uzbl.gui.main_title);
393 uzbl.gui.main_title = g_strdup (title);
398 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
401 uzbl.gui.sbar.load_progress = progress;
406 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
410 if (uzbl.behave.load_finish_handler)
411 run_handler(uzbl.behave.load_finish_handler, "");
415 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
419 if (uzbl.behave.load_start_handler)
420 run_handler(uzbl.behave.load_start_handler, "");
424 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
427 free (uzbl.state.uri);
428 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
429 uzbl.state.uri = g_string_free (newuri, FALSE);
430 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
431 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
434 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
435 if (uzbl.behave.load_commit_handler)
436 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
440 destroy_cb (GtkWidget* widget, gpointer data) {
448 if (uzbl.behave.history_handler) {
450 struct tm * timeinfo;
453 timeinfo = localtime ( &rawtime );
454 strftime (date, 80, " \"%Y-%m-%d %H:%M:%S\"", timeinfo);
455 run_handler(uzbl.behave.history_handler, date);
460 /* VIEW funcs (little webkit wrappers) */
461 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
463 VIEWFUNC(reload_bypass_cache)
464 VIEWFUNC(stop_loading)
471 /* -- command to callback/function map for things we cannot attach to any signals */
473 static struct {char *name; Command command[2];} cmdlist[] =
474 { /* key function no_split */
475 { "back", {view_go_back, 0} },
476 { "forward", {view_go_forward, 0} },
477 { "scroll_vert", {scroll_vert, 0} },
478 { "scroll_horz", {scroll_horz, 0} },
479 { "scroll_begin", {scroll_begin, 0} },
480 { "scroll_end", {scroll_end, 0} },
481 { "reload", {view_reload, 0}, },
482 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
483 { "stop", {view_stop_loading, 0}, },
484 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
485 { "zoom_out", {view_zoom_out, 0}, },
486 { "uri", {load_uri, NOSPLIT} },
487 { "js", {run_js, NOSPLIT} },
488 { "script", {run_external_js, 0} },
489 { "toggle_status", {toggle_status_cb, 0} },
490 { "spawn", {spawn, 0} },
491 { "sh", {spawn_sh, 0} },
492 { "exit", {close_uzbl, 0} },
493 { "search", {search_forward_text, NOSPLIT} },
494 { "search_reverse", {search_reverse_text, NOSPLIT} },
495 { "toggle_insert_mode", {toggle_insert_mode, 0} },
496 { "runcmd", {runcmd, NOSPLIT} }
503 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
505 for (i = 0; i < LENGTH(cmdlist); i++)
506 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
509 /* -- CORE FUNCTIONS -- */
512 free_action(gpointer act) {
513 Action *action = (Action*)act;
514 g_free(action->name);
516 g_free(action->param);
521 new_action(const gchar *name, const gchar *param) {
522 Action *action = g_new(Action, 1);
524 action->name = g_strdup(name);
526 action->param = g_strdup(param);
528 action->param = NULL;
534 file_exists (const char * filename) {
535 return (access(filename, F_OK) == 0);
539 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
543 if (argv_idx(argv, 0)) {
544 if (strcmp (argv_idx(argv, 0), "0") == 0) {
545 uzbl.behave.insert_mode = FALSE;
547 uzbl.behave.insert_mode = TRUE;
550 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
557 load_uri (WebKitWebView *web_view, GArray *argv) {
558 if (argv_idx(argv, 0)) {
559 GString* newuri = g_string_new (argv_idx(argv, 0));
560 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
561 g_string_prepend (newuri, "http://");
562 /* if we do handle cookies, ask our handler for them */
563 webkit_web_view_load_uri (web_view, newuri->str);
564 g_string_free (newuri, TRUE);
569 run_js (WebKitWebView * web_view, GArray *argv) {
570 if (argv_idx(argv, 0))
571 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
575 run_external_js (WebKitWebView * web_view, GArray *argv) {
576 if (argv_idx(argv, 0)) {
577 GArray* lines = read_file_by_line (argv_idx (argv, 0));
582 while ((line = g_array_index(lines, gchar*, i))) {
584 js = g_strdup (line);
586 gchar* newjs = g_strconcat (js, line, NULL);
592 if (uzbl.state.verbose)
593 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
595 if (argv_idx (argv, 1)) {
596 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
600 webkit_web_view_execute_script (web_view, js);
602 g_array_free (lines, TRUE);
607 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
608 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
609 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
611 if (uzbl.state.searchtx != NULL) {
612 if (uzbl.state.verbose)
613 printf ("Searching: %s\n", uzbl.state.searchtx);
615 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
616 webkit_web_view_unmark_text_matches (page);
617 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
619 if (uzbl.state.searchold != NULL)
620 g_free (uzbl.state.searchold);
622 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
625 webkit_web_view_set_highlight_text_matches (page, TRUE);
626 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
627 g_free(uzbl.state.searchtx);
628 uzbl.state.searchtx = NULL;
633 search_forward_text (WebKitWebView *page, GArray *argv) {
634 search_text(page, argv, TRUE);
638 search_reverse_text (WebKitWebView *page, GArray *argv) {
639 search_text(page, argv, FALSE);
643 new_window_load_uri (const gchar * uri) {
644 GString* to_execute = g_string_new ("");
645 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
647 for (i = 0; entries[i].long_name != NULL; i++) {
648 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
649 gchar** str = (gchar**)entries[i].arg_data;
651 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
655 if (uzbl.state.verbose)
656 printf("\n%s\n", to_execute->str);
657 g_spawn_command_line_async (to_execute->str, NULL);
658 g_string_free (to_execute, TRUE);
662 close_uzbl (WebKitWebView *page, GArray *argv) {
668 /* --Statusbar functions-- */
670 build_progressbar_ascii(int percent) {
674 GString *bar = g_string_new("");
676 l = (double)percent*((double)width/100.);
677 l = (int)(l+.5)>=(int)l ? l+.5 : l;
679 for(i=0; i<(int)l; i++)
680 g_string_append(bar, "=");
683 g_string_append(bar, "·");
685 return g_string_free(bar, FALSE);
690 const GScannerConfig scan_config = {
693 ) /* cset_skip_characters */,
698 ) /* cset_identifier_first */,
705 ) /* cset_identifier_nth */,
706 ( "" ) /* cpair_comment_single */,
708 TRUE /* case_sensitive */,
710 FALSE /* skip_comment_multi */,
711 FALSE /* skip_comment_single */,
712 FALSE /* scan_comment_multi */,
713 TRUE /* scan_identifier */,
714 TRUE /* scan_identifier_1char */,
715 FALSE /* scan_identifier_NULL */,
716 TRUE /* scan_symbols */,
717 FALSE /* scan_binary */,
718 FALSE /* scan_octal */,
719 FALSE /* scan_float */,
720 FALSE /* scan_hex */,
721 FALSE /* scan_hex_dollar */,
722 FALSE /* scan_string_sq */,
723 FALSE /* scan_string_dq */,
724 TRUE /* numbers_2_int */,
725 FALSE /* int_2_float */,
726 FALSE /* identifier_2_string */,
727 FALSE /* char_2_token */,
728 FALSE /* symbol_2_token */,
729 TRUE /* scope_0_fallback */,
734 uzbl.scan = g_scanner_new(&scan_config);
735 while(symp->symbol_name) {
736 g_scanner_scope_add_symbol(uzbl.scan, 0,
738 GINT_TO_POINTER(symp->symbol_token));
744 expand_template(const char *template) {
745 if(!template) return NULL;
747 GTokenType token = G_TOKEN_NONE;
748 GString *ret = g_string_new("");
752 g_scanner_input_text(uzbl.scan, template, strlen(template));
753 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
754 token = g_scanner_get_next_token(uzbl.scan);
756 if(token == G_TOKEN_SYMBOL) {
757 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
760 buf = uzbl.state.uri?
761 g_markup_printf_escaped("%s", uzbl.state.uri) :
763 g_string_append(ret, buf);
767 buf = itos(uzbl.gui.sbar.load_progress);
768 g_string_append(ret, buf);
771 case SYM_LOADPRGSBAR:
772 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
773 g_string_append(ret, buf);
777 buf = uzbl.gui.main_title?
778 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
780 g_string_append(ret, buf);
783 case SYM_SELECTED_URI:
784 buf = uzbl.state.selected_url?
785 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
787 g_string_append(ret, buf);
791 buf = itos(uzbl.xwin);
793 uzbl.state.instance_name?uzbl.state.instance_name:buf);
797 buf = uzbl.state.keycmd->str?
798 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
800 g_string_append(ret, buf);
805 uzbl.behave.insert_mode?"[I]":"[C]");
809 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
813 buf = itos(WEBKIT_MAJOR_VERSION);
814 g_string_append(ret, buf);
818 buf = itos(WEBKIT_MINOR_VERSION);
819 g_string_append(ret, buf);
823 buf = itos(WEBKIT_MICRO_VERSION);
824 g_string_append(ret, buf);
828 g_string_append(ret, uzbl.state.unameinfo.sysname);
831 g_string_append(ret, uzbl.state.unameinfo.nodename);
834 g_string_append(ret, uzbl.state.unameinfo.release);
837 g_string_append(ret, uzbl.state.unameinfo.version);
840 g_string_append(ret, uzbl.state.unameinfo.machine);
843 g_string_append(ret, ARCH);
847 g_string_append(ret, uzbl.state.unameinfo.domainname);
851 g_string_append(ret, COMMIT);
857 else if(token == G_TOKEN_INT) {
858 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
859 g_string_append(ret, buf);
862 else if(token == G_TOKEN_IDENTIFIER) {
863 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
865 else if(token == G_TOKEN_CHAR) {
866 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
870 return g_string_free(ret, FALSE);
872 /* --End Statusbar functions-- */
875 sharg_append(GArray *a, const gchar *str) {
876 const gchar *s = (str ? str : "");
877 g_array_append_val(a, s);
880 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
882 run_command (const gchar *command, const guint npre, const gchar **args,
883 const gboolean sync, char **stdout) {
884 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
887 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
888 gchar *pid = itos(getpid());
889 gchar *xwin = itos(uzbl.xwin);
891 sharg_append(a, command);
892 for (i = 0; i < npre; i++) /* add n args before the default vars */
893 sharg_append(a, args[i]);
894 sharg_append(a, uzbl.state.config_file);
895 sharg_append(a, pid);
896 sharg_append(a, xwin);
897 sharg_append(a, uzbl.comm.fifo_path);
898 sharg_append(a, uzbl.comm.socket_path);
899 sharg_append(a, uzbl.state.uri);
900 sharg_append(a, uzbl.gui.main_title);
902 for (i = npre; i < g_strv_length((gchar**)args); i++)
903 sharg_append(a, args[i]);
905 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
906 NULL, NULL, stdout, NULL, NULL, &err);
907 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
908 NULL, NULL, NULL, &err);
910 if (uzbl.state.verbose) {
911 GString *s = g_string_new("spawned:");
912 for (i = 0; i < (a->len); i++) {
913 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
914 g_string_append_printf(s, " %s", qarg);
917 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
918 printf("%s\n", s->str);
919 g_string_free(s, TRUE);
922 g_printerr("error on run_command: %s\n", err->message);
927 g_array_free (a, TRUE);
932 split_quoted(const gchar* src, const gboolean unquote) {
933 /* split on unquoted space, return array of strings;
934 remove a layer of quotes and backslashes if unquote */
935 if (!src) return NULL;
939 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
940 GString *s = g_string_new ("");
944 for (p = src; *p != '\0'; p++) {
945 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
946 else if (*p == '\\') { g_string_append_c(s, *p++);
947 g_string_append_c(s, *p); }
948 else if ((*p == '"') && unquote && !sq) dq = !dq;
949 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
951 else if ((*p == '\'') && unquote && !dq) sq = !sq;
952 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
954 else if ((*p == ' ') && !dq && !sq) {
955 dup = g_strdup(s->str);
956 g_array_append_val(a, dup);
957 g_string_truncate(s, 0);
958 } else g_string_append_c(s, *p);
960 dup = g_strdup(s->str);
961 g_array_append_val(a, dup);
962 ret = (gchar**)a->data;
963 g_array_free (a, FALSE);
964 g_string_free (s, TRUE);
969 spawn(WebKitWebView *web_view, GArray *argv) {
971 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
972 if (argv_idx(argv, 0))
973 run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
977 spawn_sh(WebKitWebView *web_view, GArray *argv) {
979 if (!uzbl.behave.shell_cmd) {
980 g_printerr ("spawn_sh: shell_cmd is not set!\n");
985 gchar *spacer = g_strdup("");
986 g_array_insert_val(argv, 1, spacer);
987 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
989 for (i = 1; i < g_strv_length(cmd); i++)
990 g_array_prepend_val(argv, cmd[i]);
992 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
998 parse_command(const char *cmd, const char *param) {
1001 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1004 gchar **par = split_quoted(param, TRUE);
1005 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1007 if (c[1]) { /* don't split */
1008 sharg_append(a, param);
1010 for (i = 0; i < g_strv_length(par); i++)
1011 sharg_append(a, par[i]);
1013 c[0](uzbl.gui.web_view, a);
1015 g_array_free (a, TRUE);
1018 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1021 /* command parser */
1024 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1025 G_REGEX_OPTIMIZE, 0, NULL);
1026 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1027 G_REGEX_OPTIMIZE, 0, NULL);
1028 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1029 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1030 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1031 G_REGEX_OPTIMIZE, 0, NULL);
1032 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1033 G_REGEX_OPTIMIZE, 0, NULL);
1037 get_var_value(gchar *name) {
1040 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1041 if(c->type == TYPE_STRING)
1042 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1043 else if(c->type == TYPE_INT)
1044 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1053 if(*uzbl.net.proxy_url == ' '
1054 || uzbl.net.proxy_url == NULL) {
1055 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1056 (GType) SOUP_SESSION_PROXY_URI);
1059 suri = soup_uri_new(uzbl.net.proxy_url);
1060 g_object_set(G_OBJECT(uzbl.net.soup_session),
1061 SOUP_SESSION_PROXY_URI,
1063 soup_uri_free(suri);
1070 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1071 g_array_append_val (a, uzbl.state.uri);
1072 load_uri(uzbl.gui.web_view, a);
1073 g_array_free (a, TRUE);
1077 cmd_always_insert_mode() {
1078 uzbl.behave.insert_mode =
1079 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1085 g_object_set(G_OBJECT(uzbl.net.soup_session),
1086 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1090 cmd_max_conns_host() {
1091 g_object_set(G_OBJECT(uzbl.net.soup_session),
1092 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1097 soup_session_remove_feature
1098 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1099 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1100 /*g_free(uzbl.net.soup_logger);*/
1102 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1103 soup_session_add_feature(uzbl.net.soup_session,
1104 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1108 cmd_default_font_size() {
1109 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1110 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1114 cmd_minimum_font_size() {
1115 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1116 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1123 buf = init_fifo(uzbl.behave.fifo_dir);
1124 if(uzbl.behave.fifo_dir)
1125 free(uzbl.behave.fifo_dir);
1127 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1134 buf = init_socket(uzbl.behave.fifo_dir);
1135 if(uzbl.behave.socket_dir)
1136 free(uzbl.behave.socket_dir);
1138 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1146 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1147 uzbl.behave.modmask = 0;
1149 if(uzbl.behave.modkey)
1150 free(uzbl.behave.modkey);
1152 for (i = 0; modkeys[i].key != NULL; i++) {
1153 if (g_strrstr(buf, modkeys[i].key))
1154 uzbl.behave.modmask |= modkeys[i].mask;
1162 buf = set_useragent(uzbl.net.useragent);
1163 if(uzbl.net.useragent)
1164 free(uzbl.net.useragent);
1166 uzbl.net.useragent = buf?buf:g_strdup("");
1171 gtk_widget_ref(uzbl.gui.scrolled_win);
1172 gtk_widget_ref(uzbl.gui.mainbar);
1173 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1174 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1176 if(uzbl.behave.status_top) {
1177 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1178 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1181 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1182 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1184 gtk_widget_unref(uzbl.gui.scrolled_win);
1185 gtk_widget_unref(uzbl.gui.mainbar);
1186 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1191 set_var_value(gchar *name, gchar *val) {
1193 uzbl_cmdprop *c = NULL;
1197 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1198 /* check for the variable type */
1199 if (c->type == TYPE_STRING) {
1201 *c->ptr = g_strdup(val);
1202 } else if(c->type == TYPE_INT) {
1204 *ip = (int)strtoul(val, &endp, 10);
1207 /* invoke a command specific function */
1208 if(c->func) c->func();
1210 /* this will be removed as soon as we have converted to
1211 * the callback interface
1219 runcmd(WebKitWebView* page, GArray *argv) {
1221 parse_cmd_line(argv_idx(argv, 0));
1225 parse_cmd_line(const char *ctl_line) {
1229 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1230 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1231 if(tokens[0][0] == 0) {
1232 gchar* value = parseenv (tokens[2]);
1233 set_var_value(tokens[1], value);
1238 printf("Error in command: %s\n", tokens[0]);
1241 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1242 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1243 if(tokens[0][0] == 0) {
1244 get_var_value(tokens[1]);
1248 printf("Error in command: %s\n", tokens[0]);
1251 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1252 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1253 if(tokens[0][0] == 0) {
1254 gchar* value = parseenv (tokens[2]);
1255 add_binding(tokens[1], value);
1260 printf("Error in command: %s\n", tokens[0]);
1263 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1264 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1265 if(tokens[0][0] == 0) {
1266 parse_command(tokens[1], tokens[2]);
1270 printf("Error in command: %s\n", tokens[0]);
1272 /* KEYCMD command */
1273 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1274 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1275 if(tokens[0][0] == 0) {
1276 /* should incremental commands want each individual "keystroke"
1277 sent in a loop or the whole string in one go like now? */
1278 g_string_assign(uzbl.state.keycmd, tokens[1]);
1280 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1287 else if( (ctl_line[0] == '#')
1288 || (ctl_line[0] == ' ')
1289 || (ctl_line[0] == '\n'))
1290 ; /* ignore these lines */
1292 printf("Command not understood (%s)\n", ctl_line);
1298 build_stream_name(int type, const gchar* dir) {
1300 State *s = &uzbl.state;
1303 xwin_str = itos((int)uzbl.xwin);
1305 str = g_strdup_printf
1306 ("%s/uzbl_fifo_%s", dir,
1307 s->instance_name ? s->instance_name : xwin_str);
1308 } else if (type == SOCKET) {
1309 str = g_strdup_printf
1310 ("%s/uzbl_socket_%s", dir,
1311 s->instance_name ? s->instance_name : xwin_str );
1318 control_fifo(GIOChannel *gio, GIOCondition condition) {
1319 if (uzbl.state.verbose)
1320 printf("triggered\n");
1325 if (condition & G_IO_HUP)
1326 g_error ("Fifo: Read end of pipe died!\n");
1329 g_error ("Fifo: GIOChannel broke\n");
1331 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1332 if (ret == G_IO_STATUS_ERROR) {
1333 g_error ("Fifo: Error reading: %s\n", err->message);
1337 parse_cmd_line(ctl_line);
1344 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1345 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1346 if (unlink(uzbl.comm.fifo_path) == -1)
1347 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1348 g_free(uzbl.comm.fifo_path);
1349 uzbl.comm.fifo_path = NULL;
1352 if (*dir == ' ') { /* space unsets the variable */
1356 GIOChannel *chan = NULL;
1357 GError *error = NULL;
1358 gchar *path = build_stream_name(FIFO, dir);
1360 if (!file_exists(path)) {
1361 if (mkfifo (path, 0666) == 0) {
1362 // 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.
1363 chan = g_io_channel_new_file(path, "r+", &error);
1365 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1366 if (uzbl.state.verbose)
1367 printf ("init_fifo: created successfully as %s\n", path);
1368 uzbl.comm.fifo_path = path;
1370 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1371 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1372 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1373 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1375 /* if we got this far, there was an error; cleanup */
1376 if (error) g_error_free (error);
1382 control_stdin(GIOChannel *gio, GIOCondition condition) {
1383 gchar *ctl_line = NULL;
1386 if (condition & G_IO_HUP) {
1387 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1391 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1392 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1395 parse_cmd_line(ctl_line);
1403 GIOChannel *chan = NULL;
1404 GError *error = NULL;
1406 chan = g_io_channel_unix_new(fileno(stdin));
1408 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1409 g_error ("Stdin: could not add watch\n");
1411 if (uzbl.state.verbose)
1412 printf ("Stdin: watch added successfully\n");
1415 g_error ("Stdin: Error while opening: %s\n", error->message);
1417 if (error) g_error_free (error);
1421 control_socket(GIOChannel *chan) {
1422 struct sockaddr_un remote;
1423 char buffer[512], *ctl_line;
1425 int sock, clientsock, n, done;
1428 sock = g_io_channel_unix_get_fd(chan);
1430 memset (buffer, 0, sizeof (buffer));
1432 t = sizeof (remote);
1433 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1437 memset (temp, 0, sizeof (temp));
1438 n = recv (clientsock, temp, 128, 0);
1440 buffer[strlen (buffer)] = '\0';
1444 strcat (buffer, temp);
1447 if (strcmp (buffer, "\n") < 0) {
1448 buffer[strlen (buffer) - 1] = '\0';
1450 buffer[strlen (buffer)] = '\0';
1453 ctl_line = g_strdup(buffer);
1454 parse_cmd_line (ctl_line);
1457 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1458 GError *error = NULL;
1461 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1462 if (ret == G_IO_STATUS_ERROR)
1463 g_error ("Error reading: %s\n", error->message);
1465 printf("Got line %s (%u bytes) \n",ctl_line, len);
1467 parse_line(ctl_line);
1475 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1476 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1477 if (unlink(uzbl.comm.socket_path) == -1)
1478 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1479 g_free(uzbl.comm.socket_path);
1480 uzbl.comm.socket_path = NULL;
1488 GIOChannel *chan = NULL;
1490 struct sockaddr_un local;
1491 gchar *path = build_stream_name(SOCKET, dir);
1493 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1495 local.sun_family = AF_UNIX;
1496 strcpy (local.sun_path, path);
1497 unlink (local.sun_path);
1499 len = strlen (local.sun_path) + sizeof (local.sun_family);
1500 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1501 if (uzbl.state.verbose)
1502 printf ("init_socket: opened in %s\n", path);
1505 if( (chan = g_io_channel_unix_new(sock)) ) {
1506 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1507 uzbl.comm.socket_path = path;
1510 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1512 /* if we got this far, there was an error; cleanup */
1519 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1520 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1522 // this function may be called very early when the templates are not set (yet), hence the checks
1524 update_title (void) {
1525 Behaviour *b = &uzbl.behave;
1528 if (b->show_status) {
1529 if (b->title_format_short) {
1530 parsed = expand_template(b->title_format_short);
1531 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1534 if (b->status_format) {
1535 parsed = expand_template(b->status_format);
1536 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1539 if (b->status_background) {
1541 gdk_color_parse (b->status_background, &color);
1542 //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)
1543 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1546 if (b->title_format_long) {
1547 parsed = expand_template(b->title_format_long);
1548 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1555 key_press_cb (GtkWidget* window, GdkEventKey* event)
1557 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1561 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1562 || 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)
1565 /* turn off insert mode (if always_insert_mode is not used) */
1566 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1567 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1572 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1575 if (event->keyval == GDK_Escape) {
1576 g_string_truncate(uzbl.state.keycmd, 0);
1581 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1582 if (event->keyval == GDK_Insert) {
1584 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1585 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1587 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1590 g_string_append (uzbl.state.keycmd, str);
1597 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1598 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1602 gboolean key_ret = FALSE;
1603 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1605 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1607 run_keycmd(key_ret);
1609 if (key_ret) return (!uzbl.behave.insert_mode);
1614 run_keycmd(const gboolean key_ret) {
1615 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1617 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1618 g_string_truncate(uzbl.state.keycmd, 0);
1619 parse_command(action->name, action->param);
1623 /* try if it's an incremental keycmd or one that takes args, and run it */
1624 GString* short_keys = g_string_new ("");
1625 GString* short_keys_inc = g_string_new ("");
1627 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1628 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1629 g_string_assign(short_keys_inc, short_keys->str);
1630 g_string_append_c(short_keys, '_');
1631 g_string_append_c(short_keys_inc, '*');
1633 gboolean exec_now = FALSE;
1634 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1635 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1636 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1637 if (key_ret) { /* just quit the incremental command on return */
1638 g_string_truncate(uzbl.state.keycmd, 0);
1640 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1644 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1645 GString* actionname = g_string_new ("");
1646 GString* actionparam = g_string_new ("");
1647 g_string_erase (parampart, 0, i+1);
1649 g_string_printf (actionname, action->name, parampart->str);
1651 g_string_printf (actionparam, action->param, parampart->str);
1652 parse_command(actionname->str, actionparam->str);
1653 g_string_free (actionname, TRUE);
1654 g_string_free (actionparam, TRUE);
1655 g_string_free (parampart, TRUE);
1657 g_string_truncate(uzbl.state.keycmd, 0);
1661 g_string_truncate(short_keys, short_keys->len - 1);
1663 g_string_free (short_keys, TRUE);
1664 g_string_free (short_keys_inc, TRUE);
1671 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1672 //main_window_ref = g_object_ref(scrolled_window);
1673 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
1675 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1676 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1678 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1679 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1680 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1681 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1682 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1683 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1684 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1685 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1686 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1687 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1689 return scrolled_window;
1696 g->mainbar = gtk_hbox_new (FALSE, 0);
1698 /* keep a reference to the bar so we can re-pack it at runtime*/
1699 //sbar_ref = g_object_ref(g->mainbar);
1701 g->mainbar_label = gtk_label_new ("");
1702 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1703 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1704 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1705 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1706 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1711 GtkWidget* create_window () {
1712 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1713 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1714 gtk_widget_set_name (window, "Uzbl browser");
1715 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1716 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1722 run_handler (const gchar *act, const gchar *args) {
1723 char **parts = g_strsplit(act, " ", 2);
1725 else if (g_strcmp0(parts[0], "spawn") == 0) {
1726 GString *a = g_string_new ("");
1728 spawnparts = split_quoted(parts[1], TRUE);
1729 g_string_append_printf(a, "\"%s\"", spawnparts[0]);
1730 g_string_append(a, args); /* append handler args before user args */
1732 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1733 g_string_append_printf(a, " \"%s\"", spawnparts[i]);
1734 parse_command(parts[0], a->str);
1735 g_string_free (a, TRUE);
1736 g_strfreev (spawnparts);
1738 parse_command(parts[0], parts[1]);
1743 add_binding (const gchar *key, const gchar *act) {
1744 char **parts = g_strsplit(act, " ", 2);
1751 if (uzbl.state.verbose)
1752 printf ("Binding %-10s : %s\n", key, act);
1753 action = new_action(parts[0], parts[1]);
1755 if(g_hash_table_lookup(uzbl.bindings, key))
1756 g_hash_table_remove(uzbl.bindings, key);
1757 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1763 get_xdg_var (XDG_Var xdg) {
1764 const gchar* actual_value = getenv (xdg.environmental);
1765 const gchar* home = getenv ("HOME");
1767 gchar* return_value = str_replace ("~", home, actual_value);
1769 if (! actual_value || strcmp (actual_value, "") == 0) {
1770 if (xdg.default_value) {
1771 return_value = str_replace ("~", home, xdg.default_value);
1773 return_value = NULL;
1776 return return_value;
1780 find_xdg_file (int xdg_type, char* filename) {
1781 /* xdg_type = 0 => config
1782 xdg_type = 1 => data
1783 xdg_type = 2 => cache*/
1785 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1786 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1789 gchar* temporary_string;
1793 if (! file_exists (temporary_file) && xdg_type != 2) {
1794 buf = get_xdg_var (XDG[3 + xdg_type]);
1795 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1798 while (temporary_string && ! file_exists (temporary_file)) {
1799 strcpy (temporary_file, temporary_string);
1800 strcat (temporary_file, filename);
1801 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1805 g_free (temporary_string);
1807 if (file_exists (temporary_file)) {
1808 return temporary_file;
1816 State *s = &uzbl.state;
1817 Network *n = &uzbl.net;
1819 uzbl.behave.reset_command_mode = 1;
1821 if (!s->config_file) {
1822 s->config_file = find_xdg_file (0, "/uzbl/config");
1825 if (s->config_file) {
1826 GIOChannel *chan = NULL;
1827 gchar *readbuf = NULL;
1830 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1833 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1834 == G_IO_STATUS_NORMAL) {
1835 parse_cmd_line(readbuf);
1839 g_io_channel_unref (chan);
1840 if (uzbl.state.verbose)
1841 printf ("Config %s loaded\n", s->config_file);
1843 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1846 if (uzbl.state.verbose)
1847 printf ("No configuration file loaded.\n");
1849 if (!uzbl.behave.status_format)
1850 set_var_value("status_format", STATUS_DEFAULT);
1851 if (!uzbl.behave.title_format_long)
1852 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1853 if (!uzbl.behave.title_format_short)
1854 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1857 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1861 set_useragent(gchar *val) {
1866 gchar *ua = expand_template(val);
1868 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1872 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1875 if (!uzbl.behave.cookie_handler) return;
1877 gchar * stdout = NULL;
1878 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1879 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1880 gchar *action = g_strdup ("GET");
1881 SoupURI * soup_uri = soup_message_get_uri(msg);
1882 sharg_append(a, action);
1883 sharg_append(a, soup_uri->host);
1884 sharg_append(a, soup_uri->path);
1885 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1886 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1888 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1891 g_array_free(a, TRUE);
1895 save_cookies (SoupMessage *msg, gpointer user_data){
1899 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1900 cookie = soup_cookie_to_set_cookie_header(ck->data);
1901 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1902 SoupURI * soup_uri = soup_message_get_uri(msg);
1903 gchar *action = strdup("PUT");
1904 sharg_append(a, action);
1905 sharg_append(a, soup_uri->host);
1906 sharg_append(a, soup_uri->path);
1907 sharg_append(a, cookie);
1908 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1911 g_array_free(a, TRUE);
1917 main (int argc, char* argv[]) {
1918 gtk_init (&argc, &argv);
1919 if (!g_thread_supported ())
1920 g_thread_init (NULL);
1922 uzbl.state.executable_path = g_strdup(argv[0]);
1923 uzbl.state.selected_url = NULL;
1924 uzbl.state.searchtx = NULL;
1926 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1927 g_option_context_add_main_entries (context, entries, NULL);
1928 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1929 g_option_context_parse (context, &argc, &argv, NULL);
1930 /* initialize hash table */
1931 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1933 uzbl.net.soup_session = webkit_get_default_session();
1934 uzbl.state.keycmd = g_string_new("");
1936 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1937 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1938 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1939 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1941 if(uname(&uzbl.state.unameinfo) == -1)
1942 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1947 make_var_to_name_hash();
1949 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1951 uzbl.gui.scrolled_win = create_browser();
1954 /* initial packing */
1955 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1956 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1958 uzbl.gui.main_window = create_window ();
1959 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1961 //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1963 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1964 gtk_widget_show_all (uzbl.gui.main_window);
1965 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1967 if (uzbl.state.verbose) {
1968 printf("Uzbl start location: %s\n", argv[0]);
1969 printf("window_id %i\n",(int) uzbl.xwin);
1970 printf("pid %i\n", getpid ());
1971 printf("name: %s\n", uzbl.state.instance_name);
1974 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1975 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1976 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1977 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1978 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1982 if (!uzbl.behave.show_status)
1983 gtk_widget_hide(uzbl.gui.mainbar);
1989 //if(uzbl.state.uri)
1990 // load_uri (uzbl.gui.web_view, uzbl.state.uri);
1995 return EXIT_SUCCESS;
1998 /* vi: set et ts=4: */