1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
65 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
67 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69 { NULL, 0, 0, 0, NULL, NULL, NULL }
73 /* associate command names to their properties */
74 typedef const struct {
80 enum {TYPE_INT, TYPE_STR};
82 /* an abbreviation to help keep the table's width humane */
83 #define PTR(var, t, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .func = fun }
88 } var_name_to_ptr[] = {
89 /* variable name pointer to variable in code type callback function */
90 /* --------------------------------------------------------------------------------------- */
91 { "uri", PTR(uzbl.state.uri, STR, cmd_load_uri)},
92 { "status_message", PTR(uzbl.gui.sbar.msg, STR, update_title)},
93 { "show_status", PTR(uzbl.behave.show_status, INT, cmd_set_status)},
94 { "status_top", PTR(uzbl.behave.status_top, INT, move_statusbar)},
95 { "status_format", PTR(uzbl.behave.status_format, STR, update_title)},
96 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, update_title)},
97 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, update_title)},
98 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, update_title)},
99 { "status_background", PTR(uzbl.behave.status_background, STR, update_title)},
100 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, update_title)},
101 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, update_title)},
102 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, NULL)},
103 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, cmd_always_insert_mode)},
104 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, NULL)},
105 { "modkey", PTR(uzbl.behave.modkey, STR, cmd_modkey)},
106 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, NULL)},
107 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, NULL)},
108 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, NULL)},
109 { "history_handler", PTR(uzbl.behave.history_handler, STR, NULL)},
110 { "download_handler", PTR(uzbl.behave.download_handler, STR, NULL)},
111 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, cmd_cookie_handler)},
112 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, cmd_fifo_dir)},
113 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, cmd_socket_dir)},
114 { "http_debug", PTR(uzbl.behave.http_debug, INT, cmd_http_debug)},
115 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, NULL)},
116 { "proxy_url", PTR(uzbl.net.proxy_url, STR, set_proxy_url)},
117 { "max_conns", PTR(uzbl.net.max_conns, INT, cmd_max_conns)},
118 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, cmd_max_conns_host)},
119 { "useragent", PTR(uzbl.net.useragent, STR, cmd_useragent)},
120 /* exported WebKitWebSettings properties*/
121 { "font_size", PTR(uzbl.behave.font_size, INT, cmd_font_size)},
122 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, cmd_font_size)},
123 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, cmd_minimum_font_size)},
124 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, cmd_disable_plugins)},
125 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, cmd_disable_scripts)},
126 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, cmd_autoload_img)},
127 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, cmd_autoshrink_img)},
128 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, cmd_enable_spellcheck)},
129 { "enable_private", PTR(uzbl.behave.enable_private, INT, cmd_enable_private)},
130 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, cmd_print_bg)},
131 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, cmd_style_uri)},
133 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
134 }, *n2v_p = var_name_to_ptr;
140 { "SHIFT", GDK_SHIFT_MASK }, // shift
141 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
142 { "CONTROL", GDK_CONTROL_MASK }, // control
143 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
144 { "MOD2", GDK_MOD2_MASK }, // 5th mod
145 { "MOD3", GDK_MOD3_MASK }, // 6th mod
146 { "MOD4", GDK_MOD4_MASK }, // 7th mod
147 { "MOD5", GDK_MOD5_MASK }, // 8th mod
148 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
149 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
150 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
151 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
152 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
153 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
154 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
155 { "META", GDK_META_MASK }, // meta (since 2.10)
160 /* construct a hash from the var_name_to_ptr array for quick access */
162 make_var_to_name_hash() {
163 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
165 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
171 /* --- UTILITY FUNCTIONS --- */
177 snprintf(tmp, sizeof(tmp), "%i", val);
178 return g_strdup(tmp);
182 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
185 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
188 str_replace (const char* search, const char* replace, const char* string) {
192 buf = g_strsplit (string, search, -1);
193 ret = g_strjoinv (replace, buf);
194 g_strfreev(buf); // somebody said this segfaults
200 read_file_by_line (gchar *path) {
201 GIOChannel *chan = NULL;
202 gchar *readbuf = NULL;
204 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
207 chan = g_io_channel_new_file(path, "r", NULL);
210 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
211 const gchar* val = g_strdup (readbuf);
212 g_array_append_val (lines, val);
217 g_io_channel_unref (chan);
219 fprintf(stderr, "File '%s' not be read.\n", path);
226 gchar* parseenv (char* string) {
227 extern char** environ;
228 gchar* tmpstr = NULL;
232 while (environ[i] != NULL) {
233 gchar** env = g_strsplit (environ[i], "=", 2);
234 gchar* envname = g_strconcat ("$", env[0], NULL);
236 if (g_strrstr (string, envname) != NULL) {
237 tmpstr = g_strdup(string);
239 string = str_replace(envname, env[1], tmpstr);
244 g_strfreev (env); // somebody said this breaks uzbl
252 setup_signal(int signr, sigfunc *shandler) {
253 struct sigaction nh, oh;
255 nh.sa_handler = shandler;
256 sigemptyset(&nh.sa_mask);
259 if(sigaction(signr, &nh, &oh) < 0)
267 if (uzbl.behave.fifo_dir)
268 unlink (uzbl.comm.fifo_path);
269 if (uzbl.behave.socket_dir)
270 unlink (uzbl.comm.socket_path);
272 g_free(uzbl.state.executable_path);
273 g_string_free(uzbl.state.keycmd, TRUE);
274 g_hash_table_destroy(uzbl.bindings);
275 g_hash_table_destroy(uzbl.behave.commands);
279 /* --- SIGNAL HANDLER --- */
282 catch_sigterm(int s) {
288 catch_sigint(int s) {
294 /* --- CALLBACKS --- */
297 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
300 (void) navigation_action;
301 (void) policy_decision;
303 const gchar* uri = webkit_network_request_get_uri (request);
304 if (uzbl.state.verbose)
305 printf("New window requested -> %s \n", uri);
306 new_window_load_uri(uri);
311 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
315 if (uzbl.state.selected_url != NULL) {
316 if (uzbl.state.verbose)
317 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
318 new_window_load_uri(uzbl.state.selected_url);
320 if (uzbl.state.verbose)
321 printf("New web view -> %s\n","Nothing to open, exiting");
327 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
330 if (uzbl.behave.download_handler) {
331 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
332 if (uzbl.state.verbose)
333 printf("Download -> %s\n",uri);
334 /* if urls not escaped, we may have to escape and quote uri before this call */
335 run_handler(uzbl.behave.download_handler, uri);
340 /* scroll a bar in a given direction */
342 scroll (GtkAdjustment* bar, GArray *argv) {
346 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
347 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
348 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
351 static void scroll_begin(WebKitWebView* page, GArray *argv) {
352 (void) page; (void) argv;
353 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
356 static void scroll_end(WebKitWebView* page, GArray *argv) {
357 (void) page; (void) argv;
358 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
359 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
362 static void scroll_vert(WebKitWebView* page, GArray *argv) {
364 scroll(uzbl.gui.bar_v, argv);
367 static void scroll_horz(WebKitWebView* page, GArray *argv) {
369 scroll(uzbl.gui.bar_h, argv);
374 if (!uzbl.behave.show_status) {
375 gtk_widget_hide(uzbl.gui.mainbar);
377 gtk_widget_show(uzbl.gui.mainbar);
383 toggle_status_cb (WebKitWebView* page, GArray *argv) {
387 if (uzbl.behave.show_status) {
388 gtk_widget_hide(uzbl.gui.mainbar);
390 gtk_widget_show(uzbl.gui.mainbar);
392 uzbl.behave.show_status = !uzbl.behave.show_status;
397 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
401 //Set selected_url state variable
402 g_free(uzbl.state.selected_url);
403 uzbl.state.selected_url = NULL;
405 uzbl.state.selected_url = g_strdup(link);
411 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
415 if (uzbl.gui.main_title)
416 g_free (uzbl.gui.main_title);
417 uzbl.gui.main_title = g_strdup (title);
422 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
425 uzbl.gui.sbar.load_progress = progress;
430 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
434 if (uzbl.behave.load_finish_handler)
435 run_handler(uzbl.behave.load_finish_handler, "");
439 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
443 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
444 if (uzbl.behave.load_start_handler)
445 run_handler(uzbl.behave.load_start_handler, "");
449 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
452 g_free (uzbl.state.uri);
453 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
454 uzbl.state.uri = g_string_free (newuri, FALSE);
455 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
456 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
459 if (uzbl.behave.load_commit_handler)
460 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
464 destroy_cb (GtkWidget* widget, gpointer data) {
472 if (uzbl.behave.history_handler) {
474 struct tm * timeinfo;
477 timeinfo = localtime ( &rawtime );
478 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
479 run_handler(uzbl.behave.history_handler, date);
484 /* VIEW funcs (little webkit wrappers) */
485 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
487 VIEWFUNC(reload_bypass_cache)
488 VIEWFUNC(stop_loading)
495 /* -- command to callback/function map for things we cannot attach to any signals */
497 static struct {char *name; Command command[2];} cmdlist[] =
498 { /* key function no_split */
499 { "back", {view_go_back, 0} },
500 { "forward", {view_go_forward, 0} },
501 { "scroll_vert", {scroll_vert, 0} },
502 { "scroll_horz", {scroll_horz, 0} },
503 { "scroll_begin", {scroll_begin, 0} },
504 { "scroll_end", {scroll_end, 0} },
505 { "reload", {view_reload, 0}, },
506 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
507 { "stop", {view_stop_loading, 0}, },
508 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
509 { "zoom_out", {view_zoom_out, 0}, },
510 { "uri", {load_uri, NOSPLIT} },
511 { "js", {run_js, NOSPLIT} },
512 { "script", {run_external_js, 0} },
513 { "toggle_status", {toggle_status_cb, 0} },
514 { "spawn", {spawn, 0} },
515 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
516 { "sh", {spawn_sh, 0} },
517 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
518 { "exit", {close_uzbl, 0} },
519 { "search", {search_forward_text, NOSPLIT} },
520 { "search_reverse", {search_reverse_text, NOSPLIT} },
521 { "toggle_insert_mode", {toggle_insert_mode, 0} },
522 { "runcmd", {runcmd, NOSPLIT} }
529 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
531 for (i = 0; i < LENGTH(cmdlist); i++)
532 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
535 /* -- CORE FUNCTIONS -- */
538 free_action(gpointer act) {
539 Action *action = (Action*)act;
540 g_free(action->name);
542 g_free(action->param);
547 new_action(const gchar *name, const gchar *param) {
548 Action *action = g_new(Action, 1);
550 action->name = g_strdup(name);
552 action->param = g_strdup(param);
554 action->param = NULL;
560 file_exists (const char * filename) {
561 return (access(filename, F_OK) == 0);
565 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
568 if (argv_idx(argv, 0)) {
569 if (strcmp (argv_idx(argv, 0), "0") == 0) {
570 uzbl.behave.insert_mode = FALSE;
572 uzbl.behave.insert_mode = TRUE;
575 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
582 load_uri (WebKitWebView *web_view, GArray *argv) {
583 if (argv_idx(argv, 0)) {
584 GString* newuri = g_string_new (argv_idx(argv, 0));
585 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
586 g_string_prepend (newuri, "http://");
587 /* if we do handle cookies, ask our handler for them */
588 webkit_web_view_load_uri (web_view, newuri->str);
589 g_string_free (newuri, TRUE);
594 run_js (WebKitWebView * web_view, GArray *argv) {
595 if (argv_idx(argv, 0))
596 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
600 run_external_js (WebKitWebView * web_view, GArray *argv) {
601 if (argv_idx(argv, 0)) {
602 GArray* lines = read_file_by_line (argv_idx (argv, 0));
607 while ((line = g_array_index(lines, gchar*, i))) {
609 js = g_strdup (line);
611 gchar* newjs = g_strconcat (js, line, NULL);
618 if (uzbl.state.verbose)
619 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
621 if (argv_idx (argv, 1)) {
622 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
626 webkit_web_view_execute_script (web_view, js);
628 g_array_free (lines, TRUE);
633 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
634 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
635 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
636 webkit_web_view_unmark_text_matches (page);
637 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
638 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
642 if (uzbl.state.searchtx) {
643 if (uzbl.state.verbose)
644 printf ("Searching: %s\n", uzbl.state.searchtx);
645 webkit_web_view_set_highlight_text_matches (page, TRUE);
646 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
651 search_forward_text (WebKitWebView *page, GArray *argv) {
652 search_text(page, argv, TRUE);
656 search_reverse_text (WebKitWebView *page, GArray *argv) {
657 search_text(page, argv, FALSE);
661 new_window_load_uri (const gchar * uri) {
662 GString* to_execute = g_string_new ("");
663 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
665 for (i = 0; entries[i].long_name != NULL; i++) {
666 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
667 gchar** str = (gchar**)entries[i].arg_data;
669 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
673 if (uzbl.state.verbose)
674 printf("\n%s\n", to_execute->str);
675 g_spawn_command_line_async (to_execute->str, NULL);
676 g_string_free (to_execute, TRUE);
680 close_uzbl (WebKitWebView *page, GArray *argv) {
686 /* --Statusbar functions-- */
688 build_progressbar_ascii(int percent) {
689 int width=uzbl.gui.sbar.progress_w;
692 GString *bar = g_string_new("");
694 l = (double)percent*((double)width/100.);
695 l = (int)(l+.5)>=(int)l ? l+.5 : l;
697 for(i=0; i<(int)l; i++)
698 g_string_append(bar, uzbl.gui.sbar.progress_s);
701 g_string_append(bar, uzbl.gui.sbar.progress_u);
703 return g_string_free(bar, FALSE);
708 const GScannerConfig scan_config = {
711 ) /* cset_skip_characters */,
716 ) /* cset_identifier_first */,
723 ) /* cset_identifier_nth */,
724 ( "" ) /* cpair_comment_single */,
726 TRUE /* case_sensitive */,
728 FALSE /* skip_comment_multi */,
729 FALSE /* skip_comment_single */,
730 FALSE /* scan_comment_multi */,
731 TRUE /* scan_identifier */,
732 TRUE /* scan_identifier_1char */,
733 FALSE /* scan_identifier_NULL */,
734 TRUE /* scan_symbols */,
735 FALSE /* scan_binary */,
736 FALSE /* scan_octal */,
737 FALSE /* scan_float */,
738 FALSE /* scan_hex */,
739 FALSE /* scan_hex_dollar */,
740 FALSE /* scan_string_sq */,
741 FALSE /* scan_string_dq */,
742 TRUE /* numbers_2_int */,
743 FALSE /* int_2_float */,
744 FALSE /* identifier_2_string */,
745 FALSE /* char_2_token */,
746 FALSE /* symbol_2_token */,
747 TRUE /* scope_0_fallback */,
752 uzbl.scan = g_scanner_new(&scan_config);
753 while(symp->symbol_name) {
754 g_scanner_scope_add_symbol(uzbl.scan, 0,
756 GINT_TO_POINTER(symp->symbol_token));
762 expand_template(const char *template, gboolean escape_markup) {
763 if(!template) return NULL;
765 GTokenType token = G_TOKEN_NONE;
766 GString *ret = g_string_new("");
770 g_scanner_input_text(uzbl.scan, template, strlen(template));
771 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
772 token = g_scanner_get_next_token(uzbl.scan);
774 if(token == G_TOKEN_SYMBOL) {
775 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
779 buf = uzbl.state.uri?
780 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
781 g_string_append(ret, buf);
785 g_string_append(ret, uzbl.state.uri?
786 uzbl.state.uri:g_strdup(""));
789 buf = itos(uzbl.gui.sbar.load_progress);
790 g_string_append(ret, buf);
793 case SYM_LOADPRGSBAR:
794 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
795 g_string_append(ret, buf);
800 buf = uzbl.gui.main_title?
801 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
802 g_string_append(ret, buf);
806 g_string_append(ret, uzbl.gui.main_title?
807 uzbl.gui.main_title:g_strdup(""));
809 case SYM_SELECTED_URI:
811 buf = uzbl.state.selected_url?
812 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
813 g_string_append(ret, buf);
817 g_string_append(ret, uzbl.state.selected_url?
818 uzbl.state.selected_url:g_strdup(""));
821 buf = itos(uzbl.xwin);
823 uzbl.state.instance_name?uzbl.state.instance_name:buf);
828 buf = uzbl.state.keycmd->str?
829 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
830 g_string_append(ret, buf);
834 g_string_append(ret, uzbl.state.keycmd->str?
835 uzbl.state.keycmd->str:g_strdup(""));
839 uzbl.behave.insert_mode?"[I]":"[C]");
843 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
847 buf = itos(WEBKIT_MAJOR_VERSION);
848 g_string_append(ret, buf);
852 buf = itos(WEBKIT_MINOR_VERSION);
853 g_string_append(ret, buf);
857 buf = itos(WEBKIT_MICRO_VERSION);
858 g_string_append(ret, buf);
862 g_string_append(ret, uzbl.state.unameinfo.sysname);
865 g_string_append(ret, uzbl.state.unameinfo.nodename);
868 g_string_append(ret, uzbl.state.unameinfo.release);
871 g_string_append(ret, uzbl.state.unameinfo.version);
874 g_string_append(ret, uzbl.state.unameinfo.machine);
877 g_string_append(ret, ARCH);
881 g_string_append(ret, uzbl.state.unameinfo.domainname);
885 g_string_append(ret, COMMIT);
891 else if(token == G_TOKEN_INT) {
892 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
893 g_string_append(ret, buf);
896 else if(token == G_TOKEN_IDENTIFIER) {
897 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
899 else if(token == G_TOKEN_CHAR) {
900 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
904 return g_string_free(ret, FALSE);
906 /* --End Statusbar functions-- */
909 sharg_append(GArray *a, const gchar *str) {
910 const gchar *s = (str ? str : "");
911 g_array_append_val(a, s);
914 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
916 run_command (const gchar *command, const guint npre, const gchar **args,
917 const gboolean sync, char **stdout) {
918 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
921 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
922 gchar *pid = itos(getpid());
923 gchar *xwin = itos(uzbl.xwin);
925 sharg_append(a, command);
926 for (i = 0; i < npre; i++) /* add n args before the default vars */
927 sharg_append(a, args[i]);
928 sharg_append(a, uzbl.state.config_file);
929 sharg_append(a, pid);
930 sharg_append(a, xwin);
931 sharg_append(a, uzbl.comm.fifo_path);
932 sharg_append(a, uzbl.comm.socket_path);
933 sharg_append(a, uzbl.state.uri);
934 sharg_append(a, uzbl.gui.main_title);
936 for (i = npre; i < g_strv_length((gchar**)args); i++)
937 sharg_append(a, args[i]);
941 if (*stdout) *stdout = strfree(*stdout);
943 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
944 NULL, NULL, stdout, NULL, NULL, &err);
945 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
946 NULL, NULL, NULL, &err);
948 if (uzbl.state.verbose) {
949 GString *s = g_string_new("spawned:");
950 for (i = 0; i < (a->len); i++) {
951 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
952 g_string_append_printf(s, " %s", qarg);
955 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
956 printf("%s\n", s->str);
957 g_string_free(s, TRUE);
960 g_printerr("error on run_command: %s\n", err->message);
965 g_array_free (a, TRUE);
970 split_quoted(const gchar* src, const gboolean unquote) {
971 /* split on unquoted space, return array of strings;
972 remove a layer of quotes and backslashes if unquote */
973 if (!src) return NULL;
977 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
978 GString *s = g_string_new ("");
982 for (p = src; *p != '\0'; p++) {
983 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
984 else if (*p == '\\') { g_string_append_c(s, *p++);
985 g_string_append_c(s, *p); }
986 else if ((*p == '"') && unquote && !sq) dq = !dq;
987 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
989 else if ((*p == '\'') && unquote && !dq) sq = !sq;
990 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
992 else if ((*p == ' ') && !dq && !sq) {
993 dup = g_strdup(s->str);
994 g_array_append_val(a, dup);
995 g_string_truncate(s, 0);
996 } else g_string_append_c(s, *p);
998 dup = g_strdup(s->str);
999 g_array_append_val(a, dup);
1000 ret = (gchar**)a->data;
1001 g_array_free (a, FALSE);
1002 g_string_free (s, TRUE);
1007 spawn(WebKitWebView *web_view, GArray *argv) {
1009 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1010 if (argv_idx(argv, 0))
1011 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1015 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1018 if (argv_idx(argv, 0))
1019 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1020 TRUE, &uzbl.comm.sync_stdout);
1024 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1026 if (!uzbl.behave.shell_cmd) {
1027 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1032 gchar *spacer = g_strdup("");
1033 g_array_insert_val(argv, 1, spacer);
1034 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1036 for (i = 1; i < g_strv_length(cmd); i++)
1037 g_array_prepend_val(argv, cmd[i]);
1039 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1045 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1047 if (!uzbl.behave.shell_cmd) {
1048 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1053 gchar *spacer = g_strdup("");
1054 g_array_insert_val(argv, 1, spacer);
1055 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1057 for (i = 1; i < g_strv_length(cmd); i++)
1058 g_array_prepend_val(argv, cmd[i]);
1060 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1061 TRUE, &uzbl.comm.sync_stdout);
1067 parse_command(const char *cmd, const char *param) {
1070 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1073 gchar **par = split_quoted(param, TRUE);
1074 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1076 if (c[1] == NOSPLIT) { /* don't split */
1077 sharg_append(a, param);
1079 for (i = 0; i < g_strv_length(par); i++)
1080 sharg_append(a, par[i]);
1082 c[0](uzbl.gui.web_view, a);
1084 g_array_free (a, TRUE);
1087 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1090 /* command parser */
1093 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1094 G_REGEX_OPTIMIZE, 0, NULL);
1095 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1096 G_REGEX_OPTIMIZE, 0, NULL);
1097 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1098 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1099 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1100 G_REGEX_OPTIMIZE, 0, NULL);
1101 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1102 G_REGEX_OPTIMIZE, 0, NULL);
1106 get_var_value(gchar *name) {
1109 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1110 if(c->type == TYPE_STR)
1111 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1112 else if(c->type == TYPE_INT)
1113 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1122 if(*uzbl.net.proxy_url == ' '
1123 || uzbl.net.proxy_url == NULL) {
1124 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1125 (GType) SOUP_SESSION_PROXY_URI);
1128 suri = soup_uri_new(uzbl.net.proxy_url);
1129 g_object_set(G_OBJECT(uzbl.net.soup_session),
1130 SOUP_SESSION_PROXY_URI,
1132 soup_uri_free(suri);
1139 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1140 g_array_append_val (a, uzbl.state.uri);
1141 load_uri(uzbl.gui.web_view, a);
1142 g_array_free (a, TRUE);
1146 cmd_always_insert_mode() {
1147 uzbl.behave.insert_mode =
1148 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1154 g_object_set(G_OBJECT(uzbl.net.soup_session),
1155 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1159 cmd_max_conns_host() {
1160 g_object_set(G_OBJECT(uzbl.net.soup_session),
1161 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1166 soup_session_remove_feature
1167 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1168 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1169 /*g_free(uzbl.net.soup_logger);*/
1171 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1172 soup_session_add_feature(uzbl.net.soup_session,
1173 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1176 static WebKitWebSettings*
1178 return webkit_web_view_get_settings(uzbl.gui.web_view);
1183 WebKitWebSettings *ws = view_settings();
1184 if (uzbl.behave.font_size > 0) {
1185 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1188 if (uzbl.behave.monospace_size > 0) {
1189 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1190 uzbl.behave.monospace_size, NULL);
1192 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1193 uzbl.behave.font_size, NULL);
1198 cmd_disable_plugins() {
1199 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1200 !uzbl.behave.disable_plugins, NULL);
1204 cmd_disable_scripts() {
1205 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1206 !uzbl.behave.disable_plugins, NULL);
1210 cmd_minimum_font_size() {
1211 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1212 uzbl.behave.minimum_font_size, NULL);
1215 cmd_autoload_img() {
1216 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1217 uzbl.behave.autoload_img, NULL);
1222 cmd_autoshrink_img() {
1223 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1224 uzbl.behave.autoshrink_img, NULL);
1229 cmd_enable_spellcheck() {
1230 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1231 uzbl.behave.enable_spellcheck, NULL);
1235 cmd_enable_private() {
1236 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1237 uzbl.behave.enable_private, NULL);
1242 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1243 uzbl.behave.print_bg, NULL);
1248 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1249 uzbl.behave.style_uri, NULL);
1253 cmd_cookie_handler() {
1254 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1255 if ((g_strcmp0(split[0], "sh") == 0) ||
1256 (g_strcmp0(split[0], "spawn") == 0)) {
1257 g_free (uzbl.behave.cookie_handler);
1258 uzbl.behave.cookie_handler =
1259 g_strdup_printf("sync_%s %s", split[0], split[1]);
1266 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1271 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1279 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1280 uzbl.behave.modmask = 0;
1282 if(uzbl.behave.modkey)
1283 g_free(uzbl.behave.modkey);
1284 uzbl.behave.modkey = buf;
1286 for (i = 0; modkeys[i].key != NULL; i++) {
1287 if (g_strrstr(buf, modkeys[i].key))
1288 uzbl.behave.modmask |= modkeys[i].mask;
1294 if (*uzbl.net.useragent == ' ') {
1295 g_free (uzbl.net.useragent);
1296 uzbl.net.useragent = NULL;
1298 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1300 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1301 g_free(uzbl.net.useragent);
1302 uzbl.net.useragent = ua;
1308 gtk_widget_ref(uzbl.gui.scrolled_win);
1309 gtk_widget_ref(uzbl.gui.mainbar);
1310 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1311 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1313 if(uzbl.behave.status_top) {
1314 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1315 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1318 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1319 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1321 gtk_widget_unref(uzbl.gui.scrolled_win);
1322 gtk_widget_unref(uzbl.gui.mainbar);
1323 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1328 set_var_value(gchar *name, gchar *val) {
1329 uzbl_cmdprop *c = NULL;
1332 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1333 /* check for the variable type */
1334 if (c->type == TYPE_STR) {
1336 *c->ptr = g_strdup(val);
1337 } else if(c->type == TYPE_INT) {
1338 int *ip = GPOINTER_TO_INT(c->ptr);
1339 *ip = (int)strtoul(val, &endp, 10);
1342 /* invoke a command specific function */
1343 if(c->func) c->func();
1349 runcmd(WebKitWebView* page, GArray *argv) {
1351 parse_cmd_line(argv_idx(argv, 0));
1355 parse_cmd_line(const char *ctl_line) {
1356 gchar **tokens = NULL;
1359 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1360 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1361 if(tokens[0][0] == 0) {
1362 gchar* value = parseenv(g_strdup(tokens[2]));
1363 set_var_value(tokens[1], value);
1367 printf("Error in command: %s\n", tokens[0]);
1370 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1371 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1372 if(tokens[0][0] == 0) {
1373 get_var_value(tokens[1]);
1376 printf("Error in command: %s\n", tokens[0]);
1379 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1380 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1381 if(tokens[0][0] == 0) {
1382 gchar* value = parseenv(g_strdup(tokens[2]));
1383 add_binding(tokens[1], value);
1387 printf("Error in command: %s\n", tokens[0]);
1390 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1391 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1392 if(tokens[0][0] == 0) {
1393 parse_command(tokens[1], tokens[2]);
1396 printf("Error in command: %s\n", tokens[0]);
1398 /* KEYCMD command */
1399 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1400 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1401 if(tokens[0][0] == 0) {
1402 /* should incremental commands want each individual "keystroke"
1403 sent in a loop or the whole string in one go like now? */
1404 g_string_assign(uzbl.state.keycmd, tokens[1]);
1406 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1412 else if( (ctl_line[0] == '#')
1413 || (ctl_line[0] == ' ')
1414 || (ctl_line[0] == '\n'))
1415 ; /* ignore these lines */
1417 printf("Command not understood (%s)\n", ctl_line);
1426 build_stream_name(int type, const gchar* dir) {
1428 State *s = &uzbl.state;
1431 xwin_str = itos((int)uzbl.xwin);
1433 str = g_strdup_printf
1434 ("%s/uzbl_fifo_%s", dir,
1435 s->instance_name ? s->instance_name : xwin_str);
1436 } else if (type == SOCKET) {
1437 str = g_strdup_printf
1438 ("%s/uzbl_socket_%s", dir,
1439 s->instance_name ? s->instance_name : xwin_str );
1446 control_fifo(GIOChannel *gio, GIOCondition condition) {
1447 if (uzbl.state.verbose)
1448 printf("triggered\n");
1453 if (condition & G_IO_HUP)
1454 g_error ("Fifo: Read end of pipe died!\n");
1457 g_error ("Fifo: GIOChannel broke\n");
1459 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1460 if (ret == G_IO_STATUS_ERROR) {
1461 g_error ("Fifo: Error reading: %s\n", err->message);
1465 parse_cmd_line(ctl_line);
1472 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1473 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1474 if (unlink(uzbl.comm.fifo_path) == -1)
1475 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1476 g_free(uzbl.comm.fifo_path);
1477 uzbl.comm.fifo_path = NULL;
1480 if (*dir == ' ') { /* space unsets the variable */
1485 GIOChannel *chan = NULL;
1486 GError *error = NULL;
1487 gchar *path = build_stream_name(FIFO, dir);
1489 if (!file_exists(path)) {
1490 if (mkfifo (path, 0666) == 0) {
1491 // 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.
1492 chan = g_io_channel_new_file(path, "r+", &error);
1494 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1495 if (uzbl.state.verbose)
1496 printf ("init_fifo: created successfully as %s\n", path);
1497 uzbl.comm.fifo_path = path;
1499 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1500 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1501 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1502 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1504 /* if we got this far, there was an error; cleanup */
1505 if (error) g_error_free (error);
1512 control_stdin(GIOChannel *gio, GIOCondition condition) {
1514 gchar *ctl_line = NULL;
1517 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1518 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1521 parse_cmd_line(ctl_line);
1529 GIOChannel *chan = NULL;
1530 GError *error = NULL;
1532 chan = g_io_channel_unix_new(fileno(stdin));
1534 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1535 g_error ("Stdin: could not add watch\n");
1537 if (uzbl.state.verbose)
1538 printf ("Stdin: watch added successfully\n");
1541 g_error ("Stdin: Error while opening: %s\n", error->message);
1543 if (error) g_error_free (error);
1547 control_socket(GIOChannel *chan) {
1548 struct sockaddr_un remote;
1549 char buffer[512], *ctl_line;
1551 int sock, clientsock, n, done;
1554 sock = g_io_channel_unix_get_fd(chan);
1556 memset (buffer, 0, sizeof (buffer));
1558 t = sizeof (remote);
1559 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1563 memset (temp, 0, sizeof (temp));
1564 n = recv (clientsock, temp, 128, 0);
1566 buffer[strlen (buffer)] = '\0';
1570 strcat (buffer, temp);
1573 if (strcmp (buffer, "\n") < 0) {
1574 buffer[strlen (buffer) - 1] = '\0';
1576 buffer[strlen (buffer)] = '\0';
1579 ctl_line = g_strdup(buffer);
1580 parse_cmd_line (ctl_line);
1583 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1584 GError *error = NULL;
1587 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1588 if (ret == G_IO_STATUS_ERROR)
1589 g_error ("Error reading: %s\n", error->message);
1591 printf("Got line %s (%u bytes) \n",ctl_line, len);
1593 parse_line(ctl_line);
1601 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1602 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1603 if (unlink(uzbl.comm.socket_path) == -1)
1604 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1605 g_free(uzbl.comm.socket_path);
1606 uzbl.comm.socket_path = NULL;
1614 GIOChannel *chan = NULL;
1616 struct sockaddr_un local;
1617 gchar *path = build_stream_name(SOCKET, dir);
1619 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1621 local.sun_family = AF_UNIX;
1622 strcpy (local.sun_path, path);
1623 unlink (local.sun_path);
1625 len = strlen (local.sun_path) + sizeof (local.sun_family);
1626 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1627 if (uzbl.state.verbose)
1628 printf ("init_socket: opened in %s\n", path);
1631 if( (chan = g_io_channel_unix_new(sock)) ) {
1632 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1633 uzbl.comm.socket_path = path;
1636 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1638 /* if we got this far, there was an error; cleanup */
1645 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1646 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1648 // this function may be called very early when the templates are not set (yet), hence the checks
1650 update_title (void) {
1651 Behaviour *b = &uzbl.behave;
1654 if (b->show_status) {
1655 if (b->title_format_short) {
1656 parsed = expand_template(b->title_format_short, FALSE);
1657 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1660 if (b->status_format) {
1661 parsed = expand_template(b->status_format, TRUE);
1662 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1665 if (b->status_background) {
1667 gdk_color_parse (b->status_background, &color);
1668 //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)
1669 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1672 if (b->title_format_long) {
1673 parsed = expand_template(b->title_format_long, FALSE);
1674 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1681 key_press_cb (GtkWidget* window, GdkEventKey* event)
1683 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1687 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1688 || 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)
1691 /* turn off insert mode (if always_insert_mode is not used) */
1692 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1693 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1698 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1701 if (event->keyval == GDK_Escape) {
1702 g_string_truncate(uzbl.state.keycmd, 0);
1707 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1708 if (event->keyval == GDK_Insert) {
1710 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1711 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1713 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1716 g_string_append (uzbl.state.keycmd, str);
1723 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1724 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1728 gboolean key_ret = FALSE;
1729 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1731 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1733 run_keycmd(key_ret);
1735 if (key_ret) return (!uzbl.behave.insert_mode);
1740 run_keycmd(const gboolean key_ret) {
1741 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1743 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1744 g_string_truncate(uzbl.state.keycmd, 0);
1745 parse_command(action->name, action->param);
1749 /* try if it's an incremental keycmd or one that takes args, and run it */
1750 GString* short_keys = g_string_new ("");
1751 GString* short_keys_inc = g_string_new ("");
1753 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1754 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1755 g_string_assign(short_keys_inc, short_keys->str);
1756 g_string_append_c(short_keys, '_');
1757 g_string_append_c(short_keys_inc, '*');
1759 gboolean exec_now = FALSE;
1760 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1761 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1762 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1763 if (key_ret) { /* just quit the incremental command on return */
1764 g_string_truncate(uzbl.state.keycmd, 0);
1766 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1770 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1771 GString* actionname = g_string_new ("");
1772 GString* actionparam = g_string_new ("");
1773 g_string_erase (parampart, 0, i+1);
1775 g_string_printf (actionname, action->name, parampart->str);
1777 g_string_printf (actionparam, action->param, parampart->str);
1778 parse_command(actionname->str, actionparam->str);
1779 g_string_free (actionname, TRUE);
1780 g_string_free (actionparam, TRUE);
1781 g_string_free (parampart, TRUE);
1783 g_string_truncate(uzbl.state.keycmd, 0);
1787 g_string_truncate(short_keys, short_keys->len - 1);
1789 g_string_free (short_keys, TRUE);
1790 g_string_free (short_keys_inc, TRUE);
1797 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1798 //main_window_ref = g_object_ref(scrolled_window);
1799 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
1801 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1802 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1804 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1805 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1806 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1807 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1808 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1809 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1810 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1811 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1812 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1813 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1815 return scrolled_window;
1822 g->mainbar = gtk_hbox_new (FALSE, 0);
1824 /* keep a reference to the bar so we can re-pack it at runtime*/
1825 //sbar_ref = g_object_ref(g->mainbar);
1827 g->mainbar_label = gtk_label_new ("");
1828 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1829 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1830 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1831 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1832 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1837 GtkWidget* create_window () {
1838 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1839 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1840 gtk_widget_set_name (window, "Uzbl browser");
1841 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1842 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1848 run_handler (const gchar *act, const gchar *args) {
1849 char **parts = g_strsplit(act, " ", 2);
1851 else if ((g_strcmp0(parts[0], "spawn") == 0)
1852 || (g_strcmp0(parts[0], "sh") == 0)
1853 || (g_strcmp0(parts[0], "sync_spawn") == 0)
1854 || (g_strcmp0(parts[0], "sync_sh") == 0)) {
1856 GString *a = g_string_new ("");
1858 spawnparts = split_quoted(parts[1], FALSE);
1859 g_string_append_printf(a, "%s", spawnparts[0]);
1860 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1862 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1863 g_string_append_printf(a, " %s", spawnparts[i]);
1864 parse_command(parts[0], a->str);
1865 g_string_free (a, TRUE);
1866 g_strfreev (spawnparts);
1868 parse_command(parts[0], parts[1]);
1873 add_binding (const gchar *key, const gchar *act) {
1874 char **parts = g_strsplit(act, " ", 2);
1881 if (uzbl.state.verbose)
1882 printf ("Binding %-10s : %s\n", key, act);
1883 action = new_action(parts[0], parts[1]);
1885 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1890 get_xdg_var (XDG_Var xdg) {
1891 const gchar* actual_value = getenv (xdg.environmental);
1892 const gchar* home = getenv ("HOME");
1894 gchar* return_value = str_replace ("~", home, actual_value);
1896 if (! actual_value || strcmp (actual_value, "") == 0) {
1897 if (xdg.default_value) {
1898 return_value = str_replace ("~", home, xdg.default_value);
1900 return_value = NULL;
1903 return return_value;
1907 find_xdg_file (int xdg_type, char* filename) {
1908 /* xdg_type = 0 => config
1909 xdg_type = 1 => data
1910 xdg_type = 2 => cache*/
1912 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1913 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1916 gchar* temporary_string;
1920 if (! file_exists (temporary_file) && xdg_type != 2) {
1921 buf = get_xdg_var (XDG[3 + xdg_type]);
1922 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1925 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1926 g_free (temporary_file);
1927 temporary_file = g_strconcat (temporary_string, filename, NULL);
1931 //g_free (temporary_string); - segfaults.
1933 if (file_exists (temporary_file)) {
1934 return temporary_file;
1941 State *s = &uzbl.state;
1942 Network *n = &uzbl.net;
1944 for (i = 0; default_config[i].command != NULL; i++) {
1945 parse_cmd_line(default_config[i].command);
1948 if (!s->config_file) {
1949 s->config_file = find_xdg_file (0, "/uzbl/config");
1952 if (s->config_file) {
1953 GArray* lines = read_file_by_line (s->config_file);
1957 while ((line = g_array_index(lines, gchar*, i))) {
1958 parse_cmd_line (line);
1962 g_array_free (lines, TRUE);
1964 if (uzbl.state.verbose)
1965 printf ("No configuration file loaded.\n");
1968 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1971 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1974 if (!uzbl.behave.cookie_handler) return;
1976 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1977 GString *s = g_string_new ("");
1978 SoupURI * soup_uri = soup_message_get_uri(msg);
1979 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
1980 run_handler(uzbl.behave.cookie_handler, s->str);
1982 if(uzbl.comm.sync_stdout)
1983 soup_message_headers_replace (msg->request_headers, "Cookie", uzbl.comm.sync_stdout);
1984 //printf("stdout: %s\n", uzbl.comm.sync_stdout); // debugging
1985 if (uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
1987 g_string_free(s, TRUE);
1991 save_cookies (SoupMessage *msg, gpointer user_data){
1995 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1996 cookie = soup_cookie_to_set_cookie_header(ck->data);
1997 SoupURI * soup_uri = soup_message_get_uri(msg);
1998 GString *s = g_string_new ("");
1999 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2000 run_handler(uzbl.behave.cookie_handler, s->str);
2002 g_string_free(s, TRUE);
2007 /* --- WEBINSPECTOR --- */
2009 hide_window_cb(GtkWidget *widget, gpointer data) {
2012 gtk_widget_hide(widget);
2015 static WebKitWebView*
2016 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2019 (void) web_inspector;
2020 GtkWidget* scrolled_window;
2021 GtkWidget* new_web_view;
2024 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2025 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2026 G_CALLBACK(hide_window_cb), NULL);
2028 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2029 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2030 gtk_widget_show(g->inspector_window);
2032 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2033 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2034 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2035 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2036 gtk_widget_show(scrolled_window);
2038 new_web_view = webkit_web_view_new();
2039 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2041 return WEBKIT_WEB_VIEW(new_web_view);
2045 inspector_show_window_cb (WebKitWebInspector* inspector){
2047 gtk_widget_show(uzbl.gui.inspector_window);
2051 /* TODO: Add variables and code to make use of these functions */
2053 inspector_close_window_cb (WebKitWebInspector* inspector){
2059 inspector_attach_window_cb (WebKitWebInspector* inspector){
2065 inspector_dettach_window_cb (WebKitWebInspector* inspector){
2071 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2077 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2083 set_up_inspector() {
2085 WebKitWebSettings *settings = webkit_web_settings_new();
2086 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2087 webkit_web_view_set_settings(WEBKIT_WEB_VIEW(uzbl.gui.web_view), settings);
2090 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2091 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2092 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2093 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2094 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2095 g_signal_connect (G_OBJECT (g->inspector), "dettach-window", G_CALLBACK (inspector_dettach_window_cb), NULL);
2096 g_signal_connect (G_OBJECT (g->inspector), "destroy", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2098 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2105 main (int argc, char* argv[]) {
2106 gtk_init (&argc, &argv);
2107 if (!g_thread_supported ())
2108 g_thread_init (NULL);
2109 uzbl.state.executable_path = g_strdup(argv[0]);
2110 uzbl.state.selected_url = NULL;
2111 uzbl.state.searchtx = NULL;
2113 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2114 g_option_context_add_main_entries (context, entries, NULL);
2115 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2116 g_option_context_parse (context, &argc, &argv, NULL);
2117 g_option_context_free(context);
2118 /* initialize hash table */
2119 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2121 uzbl.net.soup_session = webkit_get_default_session();
2122 uzbl.state.keycmd = g_string_new("");
2124 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2125 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2126 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2127 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2129 if(uname(&uzbl.state.unameinfo) == -1)
2130 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2132 uzbl.gui.sbar.progress_s = g_strdup("=");
2133 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2134 uzbl.gui.sbar.progress_w = 10;
2139 make_var_to_name_hash();
2141 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2143 uzbl.gui.scrolled_win = create_browser();
2146 /* initial packing */
2147 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2148 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2150 uzbl.gui.main_window = create_window ();
2151 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2154 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2155 gtk_widget_show_all (uzbl.gui.main_window);
2156 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2158 if (uzbl.state.verbose) {
2159 printf("Uzbl start location: %s\n", argv[0]);
2160 printf("window_id %i\n",(int) uzbl.xwin);
2161 printf("pid %i\n", getpid ());
2162 printf("name: %s\n", uzbl.state.instance_name);
2165 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2166 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2167 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2168 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2169 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2173 if (!uzbl.behave.show_status)
2174 gtk_widget_hide(uzbl.gui.mainbar);
2183 if(uzbl.state.uri) {
2184 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
2185 g_array_append_val(a, uzbl.state.uri);
2186 load_uri (uzbl.gui.web_view, a);
2187 g_array_free (a, TRUE);
2193 return EXIT_SUCCESS;
2196 /* vi: set et ts=4: */