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_STRING};
85 } var_name_to_ptr[] = {
86 /* variable name pointer to variable in code variable type callback function */
87 /* ------------------------------------------------------------------------------------------------------------------- */
88 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
89 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
90 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
91 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
92 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
93 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
94 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
95 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
96 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
97 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
98 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
99 { "modkey", {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
100 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
101 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
102 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
103 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
104 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
105 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
106 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
107 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
108 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
109 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
110 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
111 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
112 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
113 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
114 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
115 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
116 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
117 }, *n2v_p = var_name_to_ptr;
123 { "SHIFT", GDK_SHIFT_MASK }, // shift
124 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
125 { "CONTROL", GDK_CONTROL_MASK }, // control
126 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
127 { "MOD2", GDK_MOD2_MASK }, // 5th mod
128 { "MOD3", GDK_MOD3_MASK }, // 6th mod
129 { "MOD4", GDK_MOD4_MASK }, // 7th mod
130 { "MOD5", GDK_MOD5_MASK }, // 8th mod
131 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
132 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
133 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
134 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
135 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
136 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
137 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
138 { "META", GDK_META_MASK }, // meta (since 2.10)
143 /* construct a hash from the var_name_to_ptr array for quick access */
145 make_var_to_name_hash() {
146 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
148 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
154 /* --- UTILITY FUNCTIONS --- */
160 snprintf(tmp, sizeof(tmp), "%i", val);
161 return g_strdup(tmp);
165 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
168 str_replace (const char* search, const char* replace, const char* string) {
172 buf = g_strsplit (string, search, -1);
173 ret = g_strjoinv (replace, buf);
180 read_file_by_line (gchar *path) {
181 GIOChannel *chan = NULL;
182 gchar *readbuf = NULL;
184 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
187 chan = g_io_channel_new_file(path, "r", NULL);
190 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
191 const gchar* val = g_strdup (readbuf);
192 g_array_append_val (lines, val);
197 g_io_channel_unref (chan);
199 fprintf(stderr, "File '%s' not be read.\n", path);
206 gchar* parseenv (const char* string) {
207 extern char** environ;
208 gchar* newstring = g_strdup (string);
211 while (environ[i] != NULL) {
212 gchar** env = g_strsplit (environ[i], "=", 0);
213 gchar* envname = g_malloc (strlen (env[0]) + 1);
215 strcat (envname, "$");
216 strcat (envname, env[0]);
218 newstring = str_replace(envname, env[1], newstring);
221 //g_strfreev (env); - This still breaks uzbl, but shouldn't. The mystery thickens...
229 setup_signal(int signr, sigfunc *shandler) {
230 struct sigaction nh, oh;
232 nh.sa_handler = shandler;
233 sigemptyset(&nh.sa_mask);
236 if(sigaction(signr, &nh, &oh) < 0)
244 if (uzbl.behave.fifo_dir)
245 unlink (uzbl.comm.fifo_path);
246 if (uzbl.behave.socket_dir)
247 unlink (uzbl.comm.socket_path);
249 g_free(uzbl.state.executable_path);
250 g_string_free(uzbl.state.keycmd, TRUE);
251 g_hash_table_destroy(uzbl.bindings);
252 g_hash_table_destroy(uzbl.behave.commands);
256 /* --- SIGNAL HANDLER --- */
259 catch_sigterm(int s) {
265 catch_sigint(int s) {
271 /* --- CALLBACKS --- */
274 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
277 (void) navigation_action;
278 (void) policy_decision;
280 const gchar* uri = webkit_network_request_get_uri (request);
281 if (uzbl.state.verbose)
282 printf("New window requested -> %s \n", uri);
283 new_window_load_uri(uri);
288 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
292 if (uzbl.state.selected_url != NULL) {
293 if (uzbl.state.verbose)
294 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
295 new_window_load_uri(uzbl.state.selected_url);
297 if (uzbl.state.verbose)
298 printf("New web view -> %s\n","Nothing to open, exiting");
304 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
307 if (uzbl.behave.download_handler) {
308 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
309 if (uzbl.state.verbose)
310 printf("Download -> %s\n",uri);
311 /* if urls not escaped, we may have to escape and quote uri before this call */
312 run_handler(uzbl.behave.download_handler, uri);
317 /* scroll a bar in a given direction */
319 scroll (GtkAdjustment* bar, GArray *argv) {
323 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
324 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
325 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
328 static void scroll_begin(WebKitWebView* page, GArray *argv) {
329 (void) page; (void) argv;
330 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
333 static void scroll_end(WebKitWebView* page, GArray *argv) {
334 (void) page; (void) argv;
335 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
336 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
339 static void scroll_vert(WebKitWebView* page, GArray *argv) {
341 scroll(uzbl.gui.bar_v, argv);
344 static void scroll_horz(WebKitWebView* page, GArray *argv) {
346 scroll(uzbl.gui.bar_h, argv);
351 if (!uzbl.behave.show_status) {
352 gtk_widget_hide(uzbl.gui.mainbar);
354 gtk_widget_show(uzbl.gui.mainbar);
360 toggle_status_cb (WebKitWebView* page, GArray *argv) {
364 if (uzbl.behave.show_status) {
365 gtk_widget_hide(uzbl.gui.mainbar);
367 gtk_widget_show(uzbl.gui.mainbar);
369 uzbl.behave.show_status = !uzbl.behave.show_status;
374 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
378 //Set selected_url state variable
379 g_free(uzbl.state.selected_url);
380 uzbl.state.selected_url = NULL;
382 uzbl.state.selected_url = g_strdup(link);
388 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
392 if (uzbl.gui.main_title)
393 g_free (uzbl.gui.main_title);
394 uzbl.gui.main_title = g_strdup (title);
399 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
402 uzbl.gui.sbar.load_progress = progress;
407 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
411 if (uzbl.behave.load_finish_handler)
412 run_handler(uzbl.behave.load_finish_handler, "");
416 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
420 if (uzbl.behave.load_start_handler)
421 run_handler(uzbl.behave.load_start_handler, "");
425 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
428 g_free (uzbl.state.uri);
429 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
430 uzbl.state.uri = g_string_free (newuri, FALSE);
431 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
432 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
435 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
436 if (uzbl.behave.load_commit_handler)
437 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
441 destroy_cb (GtkWidget* widget, gpointer data) {
449 if (uzbl.behave.history_handler) {
451 struct tm * timeinfo;
454 timeinfo = localtime ( &rawtime );
455 strftime (date, 80, " \"%Y-%m-%d %H:%M:%S\"", timeinfo);
456 run_handler(uzbl.behave.history_handler, date);
461 /* VIEW funcs (little webkit wrappers) */
462 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
464 VIEWFUNC(reload_bypass_cache)
465 VIEWFUNC(stop_loading)
472 /* -- command to callback/function map for things we cannot attach to any signals */
474 static struct {char *name; Command command[2];} cmdlist[] =
475 { /* key function no_split */
476 { "back", {view_go_back, 0} },
477 { "forward", {view_go_forward, 0} },
478 { "scroll_vert", {scroll_vert, 0} },
479 { "scroll_horz", {scroll_horz, 0} },
480 { "scroll_begin", {scroll_begin, 0} },
481 { "scroll_end", {scroll_end, 0} },
482 { "reload", {view_reload, 0}, },
483 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
484 { "stop", {view_stop_loading, 0}, },
485 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
486 { "zoom_out", {view_zoom_out, 0}, },
487 { "uri", {load_uri, NOSPLIT} },
488 { "js", {run_js, NOSPLIT} },
489 { "script", {run_external_js, 0} },
490 { "toggle_status", {toggle_status_cb, 0} },
491 { "spawn", {spawn, 0} },
492 { "sh", {spawn_sh, 0} },
493 { "exit", {close_uzbl, 0} },
494 { "search", {search_forward_text, NOSPLIT} },
495 { "search_reverse", {search_reverse_text, NOSPLIT} },
496 { "toggle_insert_mode", {toggle_insert_mode, 0} },
497 { "runcmd", {runcmd, NOSPLIT} }
504 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
506 for (i = 0; i < LENGTH(cmdlist); i++)
507 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
510 /* -- CORE FUNCTIONS -- */
513 free_action(gpointer act) {
514 Action *action = (Action*)act;
515 g_free(action->name);
517 g_free(action->param);
522 new_action(const gchar *name, const gchar *param) {
523 Action *action = g_new(Action, 1);
525 action->name = g_strdup(name);
527 action->param = g_strdup(param);
529 action->param = NULL;
535 file_exists (const char * filename) {
536 return (access(filename, F_OK) == 0);
540 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
544 if (argv_idx(argv, 0)) {
545 if (strcmp (argv_idx(argv, 0), "0") == 0) {
546 uzbl.behave.insert_mode = FALSE;
548 uzbl.behave.insert_mode = TRUE;
551 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
558 load_uri (WebKitWebView *web_view, GArray *argv) {
559 if (argv_idx(argv, 0)) {
560 GString* newuri = g_string_new (argv_idx(argv, 0));
561 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
562 g_string_prepend (newuri, "http://");
563 /* if we do handle cookies, ask our handler for them */
564 webkit_web_view_load_uri (web_view, newuri->str);
565 g_string_free (newuri, TRUE);
570 run_js (WebKitWebView * web_view, GArray *argv) {
571 if (argv_idx(argv, 0))
572 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
576 run_external_js (WebKitWebView * web_view, GArray *argv) {
577 if (argv_idx(argv, 0)) {
578 GArray* lines = read_file_by_line (argv_idx (argv, 0));
583 while ((line = g_array_index(lines, gchar*, i))) {
585 js = g_strdup (line);
587 gchar* newjs = g_strconcat (js, line, NULL);
593 if (uzbl.state.verbose)
594 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
596 if (argv_idx (argv, 1)) {
597 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
601 webkit_web_view_execute_script (web_view, js);
603 g_array_free (lines, TRUE);
608 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
609 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
610 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
612 if (uzbl.state.searchtx != NULL) {
613 if (uzbl.state.verbose)
614 printf ("Searching: %s\n", uzbl.state.searchtx);
616 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
617 webkit_web_view_unmark_text_matches (page);
618 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
620 if (uzbl.state.searchold != NULL)
621 g_free (uzbl.state.searchold);
623 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
626 webkit_web_view_set_highlight_text_matches (page, TRUE);
627 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
628 g_free(uzbl.state.searchtx);
629 uzbl.state.searchtx = NULL;
634 search_forward_text (WebKitWebView *page, GArray *argv) {
635 search_text(page, argv, TRUE);
639 search_reverse_text (WebKitWebView *page, GArray *argv) {
640 search_text(page, argv, FALSE);
644 new_window_load_uri (const gchar * uri) {
645 GString* to_execute = g_string_new ("");
646 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
648 for (i = 0; entries[i].long_name != NULL; i++) {
649 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
650 gchar** str = (gchar**)entries[i].arg_data;
652 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
656 if (uzbl.state.verbose)
657 printf("\n%s\n", to_execute->str);
658 g_spawn_command_line_async (to_execute->str, NULL);
659 g_string_free (to_execute, TRUE);
663 close_uzbl (WebKitWebView *page, GArray *argv) {
669 /* --Statusbar functions-- */
671 build_progressbar_ascii(int percent) {
675 GString *bar = g_string_new("");
677 l = (double)percent*((double)width/100.);
678 l = (int)(l+.5)>=(int)l ? l+.5 : l;
680 for(i=0; i<(int)l; i++)
681 g_string_append(bar, "=");
684 g_string_append(bar, "·");
686 return g_string_free(bar, FALSE);
691 const GScannerConfig scan_config = {
694 ) /* cset_skip_characters */,
699 ) /* cset_identifier_first */,
706 ) /* cset_identifier_nth */,
707 ( "" ) /* cpair_comment_single */,
709 TRUE /* case_sensitive */,
711 FALSE /* skip_comment_multi */,
712 FALSE /* skip_comment_single */,
713 FALSE /* scan_comment_multi */,
714 TRUE /* scan_identifier */,
715 TRUE /* scan_identifier_1char */,
716 FALSE /* scan_identifier_NULL */,
717 TRUE /* scan_symbols */,
718 FALSE /* scan_binary */,
719 FALSE /* scan_octal */,
720 FALSE /* scan_float */,
721 FALSE /* scan_hex */,
722 FALSE /* scan_hex_dollar */,
723 FALSE /* scan_string_sq */,
724 FALSE /* scan_string_dq */,
725 TRUE /* numbers_2_int */,
726 FALSE /* int_2_float */,
727 FALSE /* identifier_2_string */,
728 FALSE /* char_2_token */,
729 FALSE /* symbol_2_token */,
730 TRUE /* scope_0_fallback */,
735 uzbl.scan = g_scanner_new(&scan_config);
736 while(symp->symbol_name) {
737 g_scanner_scope_add_symbol(uzbl.scan, 0,
739 GINT_TO_POINTER(symp->symbol_token));
745 expand_template(const char *template) {
746 if(!template) return NULL;
748 GTokenType token = G_TOKEN_NONE;
749 GString *ret = g_string_new("");
753 g_scanner_input_text(uzbl.scan, template, strlen(template));
754 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
755 token = g_scanner_get_next_token(uzbl.scan);
757 if(token == G_TOKEN_SYMBOL) {
758 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
761 buf = uzbl.state.uri?
762 g_markup_printf_escaped("%s", uzbl.state.uri) :
764 g_string_append(ret, buf);
768 buf = itos(uzbl.gui.sbar.load_progress);
769 g_string_append(ret, buf);
772 case SYM_LOADPRGSBAR:
773 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
774 g_string_append(ret, buf);
778 buf = uzbl.gui.main_title?
779 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
781 g_string_append(ret, buf);
784 case SYM_SELECTED_URI:
785 buf = uzbl.state.selected_url?
786 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
788 g_string_append(ret, buf);
792 buf = itos(uzbl.xwin);
794 uzbl.state.instance_name?uzbl.state.instance_name:buf);
798 buf = uzbl.state.keycmd->str?
799 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
801 g_string_append(ret, buf);
806 uzbl.behave.insert_mode?"[I]":"[C]");
810 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
814 buf = itos(WEBKIT_MAJOR_VERSION);
815 g_string_append(ret, buf);
819 buf = itos(WEBKIT_MINOR_VERSION);
820 g_string_append(ret, buf);
824 buf = itos(WEBKIT_MICRO_VERSION);
825 g_string_append(ret, buf);
829 g_string_append(ret, uzbl.state.unameinfo.sysname);
832 g_string_append(ret, uzbl.state.unameinfo.nodename);
835 g_string_append(ret, uzbl.state.unameinfo.release);
838 g_string_append(ret, uzbl.state.unameinfo.version);
841 g_string_append(ret, uzbl.state.unameinfo.machine);
844 g_string_append(ret, ARCH);
848 g_string_append(ret, uzbl.state.unameinfo.domainname);
852 g_string_append(ret, COMMIT);
858 else if(token == G_TOKEN_INT) {
859 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
860 g_string_append(ret, buf);
863 else if(token == G_TOKEN_IDENTIFIER) {
864 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
866 else if(token == G_TOKEN_CHAR) {
867 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
871 return g_string_free(ret, FALSE);
873 /* --End Statusbar functions-- */
876 sharg_append(GArray *a, const gchar *str) {
877 const gchar *s = (str ? str : "");
878 g_array_append_val(a, s);
881 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
883 run_command (const gchar *command, const guint npre, const gchar **args,
884 const gboolean sync, char **stdout) {
885 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
888 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
889 gchar *pid = itos(getpid());
890 gchar *xwin = itos(uzbl.xwin);
892 sharg_append(a, command);
893 for (i = 0; i < npre; i++) /* add n args before the default vars */
894 sharg_append(a, args[i]);
895 sharg_append(a, uzbl.state.config_file);
896 sharg_append(a, pid);
897 sharg_append(a, xwin);
898 sharg_append(a, uzbl.comm.fifo_path);
899 sharg_append(a, uzbl.comm.socket_path);
900 sharg_append(a, uzbl.state.uri);
901 sharg_append(a, uzbl.gui.main_title);
903 for (i = npre; i < g_strv_length((gchar**)args); i++)
904 sharg_append(a, args[i]);
907 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
908 NULL, NULL, stdout, NULL, NULL, &err);
909 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
910 NULL, NULL, NULL, &err);
912 if (uzbl.state.verbose) {
913 GString *s = g_string_new("spawned:");
914 for (i = 0; i < (a->len); i++) {
915 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
916 g_string_append_printf(s, " %s", qarg);
919 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
920 printf("%s\n", s->str);
921 g_string_free(s, TRUE);
924 g_printerr("error on run_command: %s\n", err->message);
929 g_array_free (a, TRUE);
934 split_quoted(const gchar* src, const gboolean unquote) {
935 /* split on unquoted space, return array of strings;
936 remove a layer of quotes and backslashes if unquote */
937 if (!src) return NULL;
941 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
942 GString *s = g_string_new ("");
946 for (p = src; *p != '\0'; p++) {
947 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
948 else if (*p == '\\') { g_string_append_c(s, *p++);
949 g_string_append_c(s, *p); }
950 else if ((*p == '"') && unquote && !sq) dq = !dq;
951 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
953 else if ((*p == '\'') && unquote && !dq) sq = !sq;
954 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
956 else if ((*p == ' ') && !dq && !sq) {
957 dup = g_strdup(s->str);
958 g_array_append_val(a, dup);
959 g_string_truncate(s, 0);
960 } else g_string_append_c(s, *p);
962 dup = g_strdup(s->str);
963 g_array_append_val(a, dup);
964 ret = (gchar**)a->data;
965 g_array_free (a, FALSE);
966 g_string_free (s, TRUE);
971 spawn(WebKitWebView *web_view, GArray *argv) {
973 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
974 if (argv_idx(argv, 0))
975 run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
979 spawn_sh(WebKitWebView *web_view, GArray *argv) {
981 if (!uzbl.behave.shell_cmd) {
982 g_printerr ("spawn_sh: shell_cmd is not set!\n");
987 gchar *spacer = g_strdup("");
988 g_array_insert_val(argv, 1, spacer);
989 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
991 for (i = 1; i < g_strv_length(cmd); i++)
992 g_array_prepend_val(argv, cmd[i]);
994 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1000 parse_command(const char *cmd, const char *param) {
1003 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1006 gchar **par = split_quoted(param, TRUE);
1007 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1009 if (c[1] == NOSPLIT) { /* don't split */
1010 sharg_append(a, param);
1012 for (i = 0; i < g_strv_length(par); i++)
1013 sharg_append(a, par[i]);
1015 c[0](uzbl.gui.web_view, a);
1017 g_array_free (a, TRUE);
1020 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1023 /* command parser */
1026 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1027 G_REGEX_OPTIMIZE, 0, NULL);
1028 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1029 G_REGEX_OPTIMIZE, 0, NULL);
1030 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1031 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1032 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1033 G_REGEX_OPTIMIZE, 0, NULL);
1034 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1035 G_REGEX_OPTIMIZE, 0, NULL);
1039 get_var_value(gchar *name) {
1042 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1043 if(c->type == TYPE_STRING)
1044 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1045 else if(c->type == TYPE_INT)
1046 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1055 if(*uzbl.net.proxy_url == ' '
1056 || uzbl.net.proxy_url == NULL) {
1057 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1058 (GType) SOUP_SESSION_PROXY_URI);
1061 suri = soup_uri_new(uzbl.net.proxy_url);
1062 g_object_set(G_OBJECT(uzbl.net.soup_session),
1063 SOUP_SESSION_PROXY_URI,
1065 soup_uri_free(suri);
1072 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1073 g_array_append_val (a, uzbl.state.uri);
1074 load_uri(uzbl.gui.web_view, a);
1075 g_array_free (a, TRUE);
1079 cmd_always_insert_mode() {
1080 uzbl.behave.insert_mode =
1081 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1087 g_object_set(G_OBJECT(uzbl.net.soup_session),
1088 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1092 cmd_max_conns_host() {
1093 g_object_set(G_OBJECT(uzbl.net.soup_session),
1094 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1099 soup_session_remove_feature
1100 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1101 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1102 /*g_free(uzbl.net.soup_logger);*/
1104 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1105 soup_session_add_feature(uzbl.net.soup_session,
1106 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1110 cmd_default_font_size() {
1111 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1112 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1116 cmd_minimum_font_size() {
1117 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1118 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1125 buf = init_fifo(uzbl.behave.fifo_dir);
1126 if(uzbl.behave.fifo_dir)
1127 g_free(uzbl.behave.fifo_dir);
1129 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1136 buf = init_socket(uzbl.behave.fifo_dir);
1137 if(uzbl.behave.socket_dir)
1138 g_free(uzbl.behave.socket_dir);
1140 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1148 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1149 uzbl.behave.modmask = 0;
1151 if(uzbl.behave.modkey)
1152 g_free(uzbl.behave.modkey);
1153 uzbl.behave.modkey = buf;
1155 for (i = 0; modkeys[i].key != NULL; i++) {
1156 if (g_strrstr(buf, modkeys[i].key))
1157 uzbl.behave.modmask |= modkeys[i].mask;
1165 buf = set_useragent(uzbl.net.useragent);
1166 if(uzbl.net.useragent)
1167 g_free(uzbl.net.useragent);
1169 uzbl.net.useragent = buf?buf:g_strdup("");
1174 gtk_widget_ref(uzbl.gui.scrolled_win);
1175 gtk_widget_ref(uzbl.gui.mainbar);
1176 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1177 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1179 if(uzbl.behave.status_top) {
1180 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1181 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1184 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1185 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1187 gtk_widget_unref(uzbl.gui.scrolled_win);
1188 gtk_widget_unref(uzbl.gui.mainbar);
1189 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1194 set_var_value(gchar *name, gchar *val) {
1195 uzbl_cmdprop *c = NULL;
1198 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1199 /* check for the variable type */
1200 if (c->type == TYPE_STRING) {
1202 *c->ptr = g_strdup(val);
1203 } else if(c->type == TYPE_INT) {
1204 int *ip = GPOINTER_TO_INT(c->ptr);
1205 *ip = (int)strtoul(val, &endp, 10);
1208 /* invoke a command specific function */
1209 if(c->func) c->func();
1215 runcmd(WebKitWebView* page, GArray *argv) {
1217 parse_cmd_line(argv_idx(argv, 0));
1221 parse_cmd_line(const char *ctl_line) {
1225 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1226 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1227 if(tokens[0][0] == 0) {
1228 gchar* value = parseenv (tokens[2]);
1229 set_var_value(tokens[1], value);
1234 printf("Error in command: %s\n", tokens[0]);
1237 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1238 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1239 if(tokens[0][0] == 0) {
1240 get_var_value(tokens[1]);
1244 printf("Error in command: %s\n", tokens[0]);
1247 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1248 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1249 if(tokens[0][0] == 0) {
1250 gchar* value = parseenv (tokens[2]);
1251 add_binding(tokens[1], value);
1256 printf("Error in command: %s\n", tokens[0]);
1259 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1260 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1261 if(tokens[0][0] == 0) {
1262 parse_command(tokens[1], tokens[2]);
1266 printf("Error in command: %s\n", tokens[0]);
1268 /* KEYCMD command */
1269 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1270 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1271 if(tokens[0][0] == 0) {
1272 /* should incremental commands want each individual "keystroke"
1273 sent in a loop or the whole string in one go like now? */
1274 g_string_assign(uzbl.state.keycmd, tokens[1]);
1276 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1283 else if( (ctl_line[0] == '#')
1284 || (ctl_line[0] == ' ')
1285 || (ctl_line[0] == '\n'))
1286 ; /* ignore these lines */
1288 printf("Command not understood (%s)\n", ctl_line);
1294 build_stream_name(int type, const gchar* dir) {
1296 State *s = &uzbl.state;
1299 xwin_str = itos((int)uzbl.xwin);
1301 str = g_strdup_printf
1302 ("%s/uzbl_fifo_%s", dir,
1303 s->instance_name ? s->instance_name : xwin_str);
1304 } else if (type == SOCKET) {
1305 str = g_strdup_printf
1306 ("%s/uzbl_socket_%s", dir,
1307 s->instance_name ? s->instance_name : xwin_str );
1314 control_fifo(GIOChannel *gio, GIOCondition condition) {
1315 if (uzbl.state.verbose)
1316 printf("triggered\n");
1321 if (condition & G_IO_HUP)
1322 g_error ("Fifo: Read end of pipe died!\n");
1325 g_error ("Fifo: GIOChannel broke\n");
1327 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1328 if (ret == G_IO_STATUS_ERROR) {
1329 g_error ("Fifo: Error reading: %s\n", err->message);
1333 parse_cmd_line(ctl_line);
1340 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1341 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1342 if (unlink(uzbl.comm.fifo_path) == -1)
1343 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1344 g_free(uzbl.comm.fifo_path);
1345 uzbl.comm.fifo_path = NULL;
1348 if (*dir == ' ') { /* space unsets the variable */
1352 GIOChannel *chan = NULL;
1353 GError *error = NULL;
1354 gchar *path = build_stream_name(FIFO, dir);
1356 if (!file_exists(path)) {
1357 if (mkfifo (path, 0666) == 0) {
1358 // 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.
1359 chan = g_io_channel_new_file(path, "r+", &error);
1361 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1362 if (uzbl.state.verbose)
1363 printf ("init_fifo: created successfully as %s\n", path);
1364 uzbl.comm.fifo_path = path;
1366 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1367 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1368 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1369 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1371 /* if we got this far, there was an error; cleanup */
1372 if (error) g_error_free (error);
1378 control_stdin(GIOChannel *gio, GIOCondition condition) {
1379 gchar *ctl_line = NULL;
1382 if (condition & G_IO_HUP) {
1383 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1387 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1388 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1391 parse_cmd_line(ctl_line);
1399 GIOChannel *chan = NULL;
1400 GError *error = NULL;
1402 chan = g_io_channel_unix_new(fileno(stdin));
1404 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1405 g_error ("Stdin: could not add watch\n");
1407 if (uzbl.state.verbose)
1408 printf ("Stdin: watch added successfully\n");
1411 g_error ("Stdin: Error while opening: %s\n", error->message);
1413 if (error) g_error_free (error);
1417 control_socket(GIOChannel *chan) {
1418 struct sockaddr_un remote;
1419 char buffer[512], *ctl_line;
1421 int sock, clientsock, n, done;
1424 sock = g_io_channel_unix_get_fd(chan);
1426 memset (buffer, 0, sizeof (buffer));
1428 t = sizeof (remote);
1429 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1433 memset (temp, 0, sizeof (temp));
1434 n = recv (clientsock, temp, 128, 0);
1436 buffer[strlen (buffer)] = '\0';
1440 strcat (buffer, temp);
1443 if (strcmp (buffer, "\n") < 0) {
1444 buffer[strlen (buffer) - 1] = '\0';
1446 buffer[strlen (buffer)] = '\0';
1449 ctl_line = g_strdup(buffer);
1450 parse_cmd_line (ctl_line);
1453 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1454 GError *error = NULL;
1457 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1458 if (ret == G_IO_STATUS_ERROR)
1459 g_error ("Error reading: %s\n", error->message);
1461 printf("Got line %s (%u bytes) \n",ctl_line, len);
1463 parse_line(ctl_line);
1471 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1472 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1473 if (unlink(uzbl.comm.socket_path) == -1)
1474 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1475 g_free(uzbl.comm.socket_path);
1476 uzbl.comm.socket_path = NULL;
1484 GIOChannel *chan = NULL;
1486 struct sockaddr_un local;
1487 gchar *path = build_stream_name(SOCKET, dir);
1489 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1491 local.sun_family = AF_UNIX;
1492 strcpy (local.sun_path, path);
1493 unlink (local.sun_path);
1495 len = strlen (local.sun_path) + sizeof (local.sun_family);
1496 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1497 if (uzbl.state.verbose)
1498 printf ("init_socket: opened in %s\n", path);
1501 if( (chan = g_io_channel_unix_new(sock)) ) {
1502 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1503 uzbl.comm.socket_path = path;
1506 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1508 /* if we got this far, there was an error; cleanup */
1515 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1516 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1518 // this function may be called very early when the templates are not set (yet), hence the checks
1520 update_title (void) {
1521 Behaviour *b = &uzbl.behave;
1524 if (b->show_status) {
1525 if (b->title_format_short) {
1526 parsed = expand_template(b->title_format_short);
1527 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1530 if (b->status_format) {
1531 parsed = expand_template(b->status_format);
1532 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1535 if (b->status_background) {
1537 gdk_color_parse (b->status_background, &color);
1538 //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)
1539 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1542 if (b->title_format_long) {
1543 parsed = expand_template(b->title_format_long);
1544 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1551 key_press_cb (GtkWidget* window, GdkEventKey* event)
1553 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1557 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1558 || 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)
1561 /* turn off insert mode (if always_insert_mode is not used) */
1562 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1563 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1568 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1571 if (event->keyval == GDK_Escape) {
1572 g_string_truncate(uzbl.state.keycmd, 0);
1577 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1578 if (event->keyval == GDK_Insert) {
1580 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1581 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1583 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1586 g_string_append (uzbl.state.keycmd, str);
1593 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1594 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1598 gboolean key_ret = FALSE;
1599 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1601 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1603 run_keycmd(key_ret);
1605 if (key_ret) return (!uzbl.behave.insert_mode);
1610 run_keycmd(const gboolean key_ret) {
1611 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1613 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1614 g_string_truncate(uzbl.state.keycmd, 0);
1615 parse_command(action->name, action->param);
1619 /* try if it's an incremental keycmd or one that takes args, and run it */
1620 GString* short_keys = g_string_new ("");
1621 GString* short_keys_inc = g_string_new ("");
1623 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1624 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1625 g_string_assign(short_keys_inc, short_keys->str);
1626 g_string_append_c(short_keys, '_');
1627 g_string_append_c(short_keys_inc, '*');
1629 gboolean exec_now = FALSE;
1630 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1631 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1632 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1633 if (key_ret) { /* just quit the incremental command on return */
1634 g_string_truncate(uzbl.state.keycmd, 0);
1636 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1640 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1641 GString* actionname = g_string_new ("");
1642 GString* actionparam = g_string_new ("");
1643 g_string_erase (parampart, 0, i+1);
1645 g_string_printf (actionname, action->name, parampart->str);
1647 g_string_printf (actionparam, action->param, parampart->str);
1648 parse_command(actionname->str, actionparam->str);
1649 g_string_free (actionname, TRUE);
1650 g_string_free (actionparam, TRUE);
1651 g_string_free (parampart, TRUE);
1653 g_string_truncate(uzbl.state.keycmd, 0);
1657 g_string_truncate(short_keys, short_keys->len - 1);
1659 g_string_free (short_keys, TRUE);
1660 g_string_free (short_keys_inc, TRUE);
1667 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1668 //main_window_ref = g_object_ref(scrolled_window);
1669 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
1671 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1672 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1674 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1675 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1676 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1677 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1678 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1679 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1680 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1681 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1682 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1683 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1685 return scrolled_window;
1692 g->mainbar = gtk_hbox_new (FALSE, 0);
1694 /* keep a reference to the bar so we can re-pack it at runtime*/
1695 //sbar_ref = g_object_ref(g->mainbar);
1697 g->mainbar_label = gtk_label_new ("");
1698 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1699 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1700 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1701 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1702 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1707 GtkWidget* create_window () {
1708 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1709 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1710 gtk_widget_set_name (window, "Uzbl browser");
1711 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1712 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1718 run_handler (const gchar *act, const gchar *args) {
1719 char **parts = g_strsplit(act, " ", 2);
1721 else if ((g_strcmp0(parts[0], "spawn") == 0)
1722 || (g_strcmp0(parts[0], "sh") == 0)) {
1724 GString *a = g_string_new ("");
1726 spawnparts = split_quoted(parts[1], FALSE);
1727 g_string_append_printf(a, "%s", spawnparts[0]);
1728 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1729 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1730 g_string_append_printf(a, " %s", spawnparts[i]);
1731 parse_command(parts[0], a->str);
1732 g_string_free (a, TRUE);
1733 g_strfreev (spawnparts);
1735 parse_command(parts[0], parts[1]);
1740 add_binding (const gchar *key, const gchar *act) {
1741 char **parts = g_strsplit(act, " ", 2);
1748 if (uzbl.state.verbose)
1749 printf ("Binding %-10s : %s\n", key, act);
1750 action = new_action(parts[0], parts[1]);
1752 if(g_hash_table_lookup(uzbl.bindings, key))
1753 g_hash_table_remove(uzbl.bindings, key);
1754 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1760 get_xdg_var (XDG_Var xdg) {
1761 const gchar* actual_value = getenv (xdg.environmental);
1762 const gchar* home = getenv ("HOME");
1764 gchar* return_value = str_replace ("~", home, actual_value);
1766 if (! actual_value || strcmp (actual_value, "") == 0) {
1767 if (xdg.default_value) {
1768 return_value = str_replace ("~", home, xdg.default_value);
1770 return_value = NULL;
1773 return return_value;
1777 find_xdg_file (int xdg_type, char* filename) {
1778 /* xdg_type = 0 => config
1779 xdg_type = 1 => data
1780 xdg_type = 2 => cache*/
1782 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1783 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1786 gchar* temporary_string;
1790 if (! file_exists (temporary_file) && xdg_type != 2) {
1791 buf = get_xdg_var (XDG[3 + xdg_type]);
1792 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1795 while (temporary_string && ! file_exists (temporary_file)) {
1796 strcpy (temporary_file, temporary_string);
1797 strcat (temporary_file, filename);
1798 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1802 g_free (temporary_string);
1804 if (file_exists (temporary_file)) {
1805 return temporary_file;
1812 State *s = &uzbl.state;
1813 Network *n = &uzbl.net;
1814 uzbl.behave.reset_command_mode = 1;
1816 if (!s->config_file) {
1817 s->config_file = find_xdg_file (0, "/uzbl/config");
1820 if (s->config_file) {
1821 GArray* lines = read_file_by_line (s->config_file);
1825 while ((line = g_array_index(lines, gchar*, i))) {
1826 parse_cmd_line (line);
1829 g_array_free (lines, TRUE);
1831 if (uzbl.state.verbose)
1832 printf ("No configuration file loaded.\n");
1834 if (!uzbl.behave.status_format)
1835 set_var_value("status_format", STATUS_DEFAULT);
1836 if (!uzbl.behave.title_format_long)
1837 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1838 if (!uzbl.behave.title_format_short)
1839 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1842 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1846 set_useragent(gchar *val) {
1851 gchar *ua = expand_template(val);
1853 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1857 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1860 if (!uzbl.behave.cookie_handler) return;
1862 gchar * stdout = NULL;
1863 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1864 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1865 gchar *action = g_strdup ("GET");
1866 SoupURI * soup_uri = soup_message_get_uri(msg);
1867 sharg_append(a, action);
1868 sharg_append(a, soup_uri->host);
1869 sharg_append(a, soup_uri->path);
1870 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1871 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1873 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1876 g_array_free(a, TRUE);
1880 save_cookies (SoupMessage *msg, gpointer user_data){
1884 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1885 cookie = soup_cookie_to_set_cookie_header(ck->data);
1886 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1887 SoupURI * soup_uri = soup_message_get_uri(msg);
1888 gchar *action = strdup("PUT");
1889 sharg_append(a, action);
1890 sharg_append(a, soup_uri->host);
1891 sharg_append(a, soup_uri->path);
1892 sharg_append(a, cookie);
1893 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1896 g_array_free(a, TRUE);
1902 main (int argc, char* argv[]) {
1903 gtk_init (&argc, &argv);
1904 if (!g_thread_supported ())
1905 g_thread_init (NULL);
1906 uzbl.state.executable_path = g_strdup(argv[0]);
1907 uzbl.state.selected_url = NULL;
1908 uzbl.state.searchtx = NULL;
1910 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1911 g_option_context_add_main_entries (context, entries, NULL);
1912 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1913 g_option_context_parse (context, &argc, &argv, NULL);
1914 /* initialize hash table */
1915 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1917 uzbl.net.soup_session = webkit_get_default_session();
1918 uzbl.state.keycmd = g_string_new("");
1920 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1921 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1922 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1923 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1925 if(uname(&uzbl.state.unameinfo) == -1)
1926 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1931 make_var_to_name_hash();
1933 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1935 uzbl.gui.scrolled_win = create_browser();
1938 /* initial packing */
1939 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1940 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1942 uzbl.gui.main_window = create_window ();
1943 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1945 //load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1947 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1948 gtk_widget_show_all (uzbl.gui.main_window);
1949 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1951 if (uzbl.state.verbose) {
1952 printf("Uzbl start location: %s\n", argv[0]);
1953 printf("window_id %i\n",(int) uzbl.xwin);
1954 printf("pid %i\n", getpid ());
1955 printf("name: %s\n", uzbl.state.instance_name);
1958 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1959 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1960 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1961 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1962 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1966 if (!uzbl.behave.show_status)
1967 gtk_widget_hide(uzbl.gui.mainbar);
1973 if(uzbl.state.uri) {
1974 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1975 g_array_append_val(a, uzbl.state.uri);
1976 load_uri (uzbl.gui.web_view, a);
1977 g_array_free (a, TRUE);
1983 return EXIT_SUCCESS;
1986 /* vi: set et ts=4: */