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);
174 //g_strfreev(buf); - segfaults.
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_strconcat ("$", env[0], NULL);
215 if (g_strrstr (newstring, envname)) {
216 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 g_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) {
542 if (argv_idx(argv, 0)) {
543 if (strcmp (argv_idx(argv, 0), "0") == 0) {
544 uzbl.behave.insert_mode = FALSE;
546 uzbl.behave.insert_mode = TRUE;
549 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
556 load_uri (WebKitWebView *web_view, GArray *argv) {
557 if (argv_idx(argv, 0)) {
558 GString* newuri = g_string_new (argv_idx(argv, 0));
559 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
560 g_string_prepend (newuri, "http://");
561 /* if we do handle cookies, ask our handler for them */
562 webkit_web_view_load_uri (web_view, newuri->str);
563 g_string_free (newuri, TRUE);
568 run_js (WebKitWebView * web_view, GArray *argv) {
569 if (argv_idx(argv, 0))
570 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
574 run_external_js (WebKitWebView * web_view, GArray *argv) {
575 if (argv_idx(argv, 0)) {
576 GArray* lines = read_file_by_line (argv_idx (argv, 0));
581 while ((line = g_array_index(lines, gchar*, i))) {
583 js = g_strdup (line);
585 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]);
906 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
907 NULL, NULL, stdout, NULL, NULL, &err);
908 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
909 NULL, NULL, NULL, &err);
911 if (uzbl.state.verbose) {
912 GString *s = g_string_new("spawned:");
913 for (i = 0; i < (a->len); i++) {
914 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
915 g_string_append_printf(s, " %s", qarg);
918 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
919 printf("%s\n", s->str);
920 g_string_free(s, TRUE);
923 g_printerr("error on run_command: %s\n", err->message);
928 g_array_free (a, TRUE);
933 split_quoted(const gchar* src, const gboolean unquote) {
934 /* split on unquoted space, return array of strings;
935 remove a layer of quotes and backslashes if unquote */
936 if (!src) return NULL;
940 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
941 GString *s = g_string_new ("");
945 for (p = src; *p != '\0'; p++) {
946 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
947 else if (*p == '\\') { g_string_append_c(s, *p++);
948 g_string_append_c(s, *p); }
949 else if ((*p == '"') && unquote && !sq) dq = !dq;
950 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
952 else if ((*p == '\'') && unquote && !dq) sq = !sq;
953 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
955 else if ((*p == ' ') && !dq && !sq) {
956 dup = g_strdup(s->str);
957 g_array_append_val(a, dup);
958 g_string_truncate(s, 0);
959 } else g_string_append_c(s, *p);
961 dup = g_strdup(s->str);
962 g_array_append_val(a, dup);
963 ret = (gchar**)a->data;
964 g_array_free (a, FALSE);
965 g_string_free (s, TRUE);
970 spawn(WebKitWebView *web_view, GArray *argv) {
972 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
973 if (argv_idx(argv, 0))
974 run_command(argv_idx(argv, 0), 0, (const gchar **) argv->data + sizeof(gchar*), FALSE, NULL);
978 spawn_sh(WebKitWebView *web_view, GArray *argv) {
980 if (!uzbl.behave.shell_cmd) {
981 g_printerr ("spawn_sh: shell_cmd is not set!\n");
986 gchar *spacer = g_strdup("");
987 g_array_insert_val(argv, 1, spacer);
988 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
990 for (i = 1; i < g_strv_length(cmd); i++)
991 g_array_prepend_val(argv, cmd[i]);
993 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
999 parse_command(const char *cmd, const char *param) {
1002 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1005 gchar **par = split_quoted(param, TRUE);
1006 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1008 if (c[1] == NOSPLIT) { /* don't split */
1009 sharg_append(a, param);
1011 for (i = 0; i < g_strv_length(par); i++)
1012 sharg_append(a, par[i]);
1014 c[0](uzbl.gui.web_view, a);
1016 g_array_free (a, TRUE);
1019 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1022 /* command parser */
1025 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1026 G_REGEX_OPTIMIZE, 0, NULL);
1027 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1028 G_REGEX_OPTIMIZE, 0, NULL);
1029 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1030 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1031 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1032 G_REGEX_OPTIMIZE, 0, NULL);
1033 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1034 G_REGEX_OPTIMIZE, 0, NULL);
1038 get_var_value(gchar *name) {
1041 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1042 if(c->type == TYPE_STRING)
1043 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1044 else if(c->type == TYPE_INT)
1045 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1054 if(*uzbl.net.proxy_url == ' '
1055 || uzbl.net.proxy_url == NULL) {
1056 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1057 (GType) SOUP_SESSION_PROXY_URI);
1060 suri = soup_uri_new(uzbl.net.proxy_url);
1061 g_object_set(G_OBJECT(uzbl.net.soup_session),
1062 SOUP_SESSION_PROXY_URI,
1064 soup_uri_free(suri);
1071 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1072 g_array_append_val (a, uzbl.state.uri);
1073 load_uri(uzbl.gui.web_view, a);
1074 g_array_free (a, TRUE);
1078 cmd_always_insert_mode() {
1079 uzbl.behave.insert_mode =
1080 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1086 g_object_set(G_OBJECT(uzbl.net.soup_session),
1087 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1091 cmd_max_conns_host() {
1092 g_object_set(G_OBJECT(uzbl.net.soup_session),
1093 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1098 soup_session_remove_feature
1099 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1100 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1101 /*g_free(uzbl.net.soup_logger);*/
1103 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1104 soup_session_add_feature(uzbl.net.soup_session,
1105 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1109 cmd_default_font_size() {
1110 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1111 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1115 cmd_minimum_font_size() {
1116 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1117 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1124 buf = init_fifo(uzbl.behave.fifo_dir);
1125 if(uzbl.behave.fifo_dir)
1126 g_free(uzbl.behave.fifo_dir);
1128 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1135 buf = init_socket(uzbl.behave.fifo_dir);
1136 if(uzbl.behave.socket_dir)
1137 g_free(uzbl.behave.socket_dir);
1139 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1147 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1148 uzbl.behave.modmask = 0;
1150 if(uzbl.behave.modkey)
1151 g_free(uzbl.behave.modkey);
1152 uzbl.behave.modkey = buf;
1154 for (i = 0; modkeys[i].key != NULL; i++) {
1155 if (g_strrstr(buf, modkeys[i].key))
1156 uzbl.behave.modmask |= modkeys[i].mask;
1164 buf = set_useragent(uzbl.net.useragent);
1165 if(uzbl.net.useragent)
1166 g_free(uzbl.net.useragent);
1168 uzbl.net.useragent = buf?buf:g_strdup("");
1173 gtk_widget_ref(uzbl.gui.scrolled_win);
1174 gtk_widget_ref(uzbl.gui.mainbar);
1175 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1176 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1178 if(uzbl.behave.status_top) {
1179 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1180 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1183 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.mainbar, FALSE, TRUE, 0);
1186 gtk_widget_unref(uzbl.gui.scrolled_win);
1187 gtk_widget_unref(uzbl.gui.mainbar);
1188 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1193 set_var_value(gchar *name, gchar *val) {
1194 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) {
1203 int *ip = GPOINTER_TO_INT(c->ptr);
1204 *ip = (int)strtoul(val, &endp, 10);
1207 /* invoke a command specific function */
1208 if(c->func) c->func();
1214 runcmd(WebKitWebView* page, GArray *argv) {
1216 parse_cmd_line(argv_idx(argv, 0));
1220 parse_cmd_line(const char *ctl_line) {
1224 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1225 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1226 if(tokens[0][0] == 0) {
1227 gchar* value = parseenv (tokens[2]);
1228 set_var_value(tokens[1], value);
1233 printf("Error in command: %s\n", tokens[0]);
1236 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1237 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1238 if(tokens[0][0] == 0) {
1239 get_var_value(tokens[1]);
1243 printf("Error in command: %s\n", tokens[0]);
1246 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1247 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1248 if(tokens[0][0] == 0) {
1249 gchar* value = parseenv (tokens[2]);
1250 add_binding(tokens[1], value);
1255 printf("Error in command: %s\n", tokens[0]);
1258 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1259 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1260 if(tokens[0][0] == 0) {
1261 parse_command(tokens[1], tokens[2]);
1265 printf("Error in command: %s\n", tokens[0]);
1267 /* KEYCMD command */
1268 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1269 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1270 if(tokens[0][0] == 0) {
1271 /* should incremental commands want each individual "keystroke"
1272 sent in a loop or the whole string in one go like now? */
1273 g_string_assign(uzbl.state.keycmd, tokens[1]);
1275 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1282 else if( (ctl_line[0] == '#')
1283 || (ctl_line[0] == ' ')
1284 || (ctl_line[0] == '\n'))
1285 ; /* ignore these lines */
1287 printf("Command not understood (%s)\n", ctl_line);
1293 build_stream_name(int type, const gchar* dir) {
1295 State *s = &uzbl.state;
1298 xwin_str = itos((int)uzbl.xwin);
1300 str = g_strdup_printf
1301 ("%s/uzbl_fifo_%s", dir,
1302 s->instance_name ? s->instance_name : xwin_str);
1303 } else if (type == SOCKET) {
1304 str = g_strdup_printf
1305 ("%s/uzbl_socket_%s", dir,
1306 s->instance_name ? s->instance_name : xwin_str );
1313 control_fifo(GIOChannel *gio, GIOCondition condition) {
1314 if (uzbl.state.verbose)
1315 printf("triggered\n");
1320 if (condition & G_IO_HUP)
1321 g_error ("Fifo: Read end of pipe died!\n");
1324 g_error ("Fifo: GIOChannel broke\n");
1326 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1327 if (ret == G_IO_STATUS_ERROR) {
1328 g_error ("Fifo: Error reading: %s\n", err->message);
1332 parse_cmd_line(ctl_line);
1339 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1340 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1341 if (unlink(uzbl.comm.fifo_path) == -1)
1342 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1343 g_free(uzbl.comm.fifo_path);
1344 uzbl.comm.fifo_path = NULL;
1347 if (*dir == ' ') { /* space unsets the variable */
1351 GIOChannel *chan = NULL;
1352 GError *error = NULL;
1353 gchar *path = build_stream_name(FIFO, dir);
1355 if (!file_exists(path)) {
1356 if (mkfifo (path, 0666) == 0) {
1357 // 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.
1358 chan = g_io_channel_new_file(path, "r+", &error);
1360 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1361 if (uzbl.state.verbose)
1362 printf ("init_fifo: created successfully as %s\n", path);
1363 uzbl.comm.fifo_path = path;
1365 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1366 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1367 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1368 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1370 /* if we got this far, there was an error; cleanup */
1371 if (error) g_error_free (error);
1377 control_stdin(GIOChannel *gio, GIOCondition condition) {
1378 gchar *ctl_line = NULL;
1381 if (condition & G_IO_HUP) {
1382 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1386 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1387 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1390 parse_cmd_line(ctl_line);
1398 GIOChannel *chan = NULL;
1399 GError *error = NULL;
1401 chan = g_io_channel_unix_new(fileno(stdin));
1403 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1404 g_error ("Stdin: could not add watch\n");
1406 if (uzbl.state.verbose)
1407 printf ("Stdin: watch added successfully\n");
1410 g_error ("Stdin: Error while opening: %s\n", error->message);
1412 if (error) g_error_free (error);
1416 control_socket(GIOChannel *chan) {
1417 struct sockaddr_un remote;
1418 char buffer[512], *ctl_line;
1420 int sock, clientsock, n, done;
1423 sock = g_io_channel_unix_get_fd(chan);
1425 memset (buffer, 0, sizeof (buffer));
1427 t = sizeof (remote);
1428 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1432 memset (temp, 0, sizeof (temp));
1433 n = recv (clientsock, temp, 128, 0);
1435 buffer[strlen (buffer)] = '\0';
1439 strcat (buffer, temp);
1442 if (strcmp (buffer, "\n") < 0) {
1443 buffer[strlen (buffer) - 1] = '\0';
1445 buffer[strlen (buffer)] = '\0';
1448 ctl_line = g_strdup(buffer);
1449 parse_cmd_line (ctl_line);
1452 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1453 GError *error = NULL;
1456 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1457 if (ret == G_IO_STATUS_ERROR)
1458 g_error ("Error reading: %s\n", error->message);
1460 printf("Got line %s (%u bytes) \n",ctl_line, len);
1462 parse_line(ctl_line);
1470 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1471 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1472 if (unlink(uzbl.comm.socket_path) == -1)
1473 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1474 g_free(uzbl.comm.socket_path);
1475 uzbl.comm.socket_path = NULL;
1483 GIOChannel *chan = NULL;
1485 struct sockaddr_un local;
1486 gchar *path = build_stream_name(SOCKET, dir);
1488 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1490 local.sun_family = AF_UNIX;
1491 strcpy (local.sun_path, path);
1492 unlink (local.sun_path);
1494 len = strlen (local.sun_path) + sizeof (local.sun_family);
1495 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1496 if (uzbl.state.verbose)
1497 printf ("init_socket: opened in %s\n", path);
1500 if( (chan = g_io_channel_unix_new(sock)) ) {
1501 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1502 uzbl.comm.socket_path = path;
1505 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1507 /* if we got this far, there was an error; cleanup */
1514 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1515 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1517 // this function may be called very early when the templates are not set (yet), hence the checks
1519 update_title (void) {
1520 Behaviour *b = &uzbl.behave;
1523 if (b->show_status) {
1524 if (b->title_format_short) {
1525 parsed = expand_template(b->title_format_short);
1526 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1529 if (b->status_format) {
1530 parsed = expand_template(b->status_format);
1531 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1534 if (b->status_background) {
1536 gdk_color_parse (b->status_background, &color);
1537 //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)
1538 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1541 if (b->title_format_long) {
1542 parsed = expand_template(b->title_format_long);
1543 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1550 key_press_cb (GtkWidget* window, GdkEventKey* event)
1552 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1556 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1557 || 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)
1560 /* turn off insert mode (if always_insert_mode is not used) */
1561 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1562 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1567 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1570 if (event->keyval == GDK_Escape) {
1571 g_string_truncate(uzbl.state.keycmd, 0);
1576 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1577 if (event->keyval == GDK_Insert) {
1579 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1580 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1582 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1585 g_string_append (uzbl.state.keycmd, str);
1592 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1593 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1597 gboolean key_ret = FALSE;
1598 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1600 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1602 run_keycmd(key_ret);
1604 if (key_ret) return (!uzbl.behave.insert_mode);
1609 run_keycmd(const gboolean key_ret) {
1610 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1612 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1613 g_string_truncate(uzbl.state.keycmd, 0);
1614 parse_command(action->name, action->param);
1618 /* try if it's an incremental keycmd or one that takes args, and run it */
1619 GString* short_keys = g_string_new ("");
1620 GString* short_keys_inc = g_string_new ("");
1622 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1623 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1624 g_string_assign(short_keys_inc, short_keys->str);
1625 g_string_append_c(short_keys, '_');
1626 g_string_append_c(short_keys_inc, '*');
1628 gboolean exec_now = FALSE;
1629 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1630 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1631 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1632 if (key_ret) { /* just quit the incremental command on return */
1633 g_string_truncate(uzbl.state.keycmd, 0);
1635 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1639 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1640 GString* actionname = g_string_new ("");
1641 GString* actionparam = g_string_new ("");
1642 g_string_erase (parampart, 0, i+1);
1644 g_string_printf (actionname, action->name, parampart->str);
1646 g_string_printf (actionparam, action->param, parampart->str);
1647 parse_command(actionname->str, actionparam->str);
1648 g_string_free (actionname, TRUE);
1649 g_string_free (actionparam, TRUE);
1650 g_string_free (parampart, TRUE);
1652 g_string_truncate(uzbl.state.keycmd, 0);
1656 g_string_truncate(short_keys, short_keys->len - 1);
1658 g_string_free (short_keys, TRUE);
1659 g_string_free (short_keys_inc, TRUE);
1666 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1667 //main_window_ref = g_object_ref(scrolled_window);
1668 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
1670 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1671 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1673 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1674 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1675 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1676 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1677 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1678 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1679 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1680 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1681 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1682 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1684 return scrolled_window;
1691 g->mainbar = gtk_hbox_new (FALSE, 0);
1693 /* keep a reference to the bar so we can re-pack it at runtime*/
1694 //sbar_ref = g_object_ref(g->mainbar);
1696 g->mainbar_label = gtk_label_new ("");
1697 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1698 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1699 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1700 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1701 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1706 GtkWidget* create_window () {
1707 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1708 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1709 gtk_widget_set_name (window, "Uzbl browser");
1710 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1711 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1717 run_handler (const gchar *act, const gchar *args) {
1718 char **parts = g_strsplit(act, " ", 2);
1720 else if ((g_strcmp0(parts[0], "spawn") == 0)
1721 || (g_strcmp0(parts[0], "sh") == 0)) {
1723 GString *a = g_string_new ("");
1725 spawnparts = split_quoted(parts[1], FALSE);
1726 g_string_append_printf(a, "%s", spawnparts[0]);
1727 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1728 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1729 g_string_append_printf(a, " %s", spawnparts[i]);
1730 parse_command(parts[0], a->str);
1731 g_string_free (a, TRUE);
1732 g_strfreev (spawnparts);
1734 parse_command(parts[0], parts[1]);
1739 add_binding (const gchar *key, const gchar *act) {
1740 char **parts = g_strsplit(act, " ", 2);
1747 if (uzbl.state.verbose)
1748 printf ("Binding %-10s : %s\n", key, act);
1749 action = new_action(parts[0], parts[1]);
1751 if(g_hash_table_lookup(uzbl.bindings, key))
1752 g_hash_table_remove(uzbl.bindings, key);
1753 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1759 get_xdg_var (XDG_Var xdg) {
1760 const gchar* actual_value = getenv (xdg.environmental);
1761 const gchar* home = getenv ("HOME");
1763 gchar* return_value = str_replace ("~", home, actual_value);
1765 if (! actual_value || strcmp (actual_value, "") == 0) {
1766 if (xdg.default_value) {
1767 return_value = str_replace ("~", home, xdg.default_value);
1769 return_value = NULL;
1772 return return_value;
1776 find_xdg_file (int xdg_type, char* filename) {
1777 /* xdg_type = 0 => config
1778 xdg_type = 1 => data
1779 xdg_type = 2 => cache*/
1781 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1782 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1785 gchar* temporary_string;
1789 if (! file_exists (temporary_file) && xdg_type != 2) {
1790 buf = get_xdg_var (XDG[3 + xdg_type]);
1791 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1794 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1795 g_free (temporary_file);
1796 temporary_file = g_strconcat (temporary_string, filename, NULL);
1800 //g_free (temporary_string); - segfaults.
1802 if (file_exists (temporary_file)) {
1803 return temporary_file;
1810 State *s = &uzbl.state;
1811 Network *n = &uzbl.net;
1812 uzbl.behave.reset_command_mode = 1;
1814 if (!s->config_file) {
1815 s->config_file = find_xdg_file (0, "/uzbl/config");
1818 if (s->config_file) {
1819 GArray* lines = read_file_by_line (s->config_file);
1823 while ((line = g_array_index(lines, gchar*, i))) {
1824 parse_cmd_line (line);
1828 g_array_free (lines, TRUE);
1830 if (uzbl.state.verbose)
1831 printf ("No configuration file loaded.\n");
1833 if (!uzbl.behave.status_format)
1834 set_var_value("status_format", STATUS_DEFAULT);
1835 if (!uzbl.behave.title_format_long)
1836 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1837 if (!uzbl.behave.title_format_short)
1838 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1841 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1845 set_useragent(gchar *val) {
1850 gchar *ua = expand_template(val);
1852 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1856 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1859 if (!uzbl.behave.cookie_handler) return;
1861 gchar * stdout = NULL;
1862 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1863 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1864 gchar *action = g_strdup ("GET");
1865 SoupURI * soup_uri = soup_message_get_uri(msg);
1866 sharg_append(a, action);
1867 sharg_append(a, soup_uri->host);
1868 sharg_append(a, soup_uri->path);
1869 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1870 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1872 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1875 g_array_free(a, TRUE);
1879 save_cookies (SoupMessage *msg, gpointer user_data){
1883 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1884 cookie = soup_cookie_to_set_cookie_header(ck->data);
1885 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1886 SoupURI * soup_uri = soup_message_get_uri(msg);
1887 gchar *action = strdup("PUT");
1888 sharg_append(a, action);
1889 sharg_append(a, soup_uri->host);
1890 sharg_append(a, soup_uri->path);
1891 sharg_append(a, cookie);
1892 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1895 g_array_free(a, TRUE);
1901 main (int argc, char* argv[]) {
1902 gtk_init (&argc, &argv);
1903 if (!g_thread_supported ())
1904 g_thread_init (NULL);
1905 uzbl.state.executable_path = g_strdup(argv[0]);
1906 uzbl.state.selected_url = NULL;
1907 uzbl.state.searchtx = NULL;
1909 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1910 g_option_context_add_main_entries (context, entries, NULL);
1911 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1912 g_option_context_parse (context, &argc, &argv, NULL);
1913 g_option_context_free(context);
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);
1946 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1947 gtk_widget_show_all (uzbl.gui.main_window);
1948 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1950 if (uzbl.state.verbose) {
1951 printf("Uzbl start location: %s\n", argv[0]);
1952 printf("window_id %i\n",(int) uzbl.xwin);
1953 printf("pid %i\n", getpid ());
1954 printf("name: %s\n", uzbl.state.instance_name);
1957 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1958 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1959 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1960 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1961 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1965 if (!uzbl.behave.show_status)
1966 gtk_widget_hide(uzbl.gui.mainbar);
1972 if(uzbl.state.uri) {
1973 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1974 g_array_append_val(a, uzbl.state.uri);
1975 load_uri (uzbl.gui.web_view, a);
1976 g_array_free (a, TRUE);
1982 return EXIT_SUCCESS;
1985 /* vi: set et ts=4: */