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
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { NULL, 0, 0, 0, NULL, NULL, NULL }
79 /* associate command names to their properties */
80 typedef const struct {
87 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
89 /* an abbreviation to help keep the table's width humane */
90 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
95 } var_name_to_ptr[] = {
96 /* variable name pointer to variable in code type dump callback function */
97 /* --------------------------------------------------------------------------------------- */
98 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
99 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
100 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
101 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
102 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
103 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
104 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
105 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
106 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
107 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
108 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
109 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
110 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
111 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
112 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
113 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
114 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
115 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
116 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
117 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
118 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
119 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
120 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
121 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
122 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
123 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
124 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
125 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
126 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
127 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
128 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
129 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
130 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
131 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
132 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
133 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
134 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
135 /* exported WebKitWebSettings properties */
136 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
137 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
138 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
139 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
140 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
141 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
142 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
143 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
144 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
145 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
146 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
147 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
148 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
149 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
150 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
151 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
153 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
154 }, *n2v_p = var_name_to_ptr;
160 { "SHIFT", GDK_SHIFT_MASK }, // shift
161 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
162 { "CONTROL", GDK_CONTROL_MASK }, // control
163 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
164 { "MOD2", GDK_MOD2_MASK }, // 5th mod
165 { "MOD3", GDK_MOD3_MASK }, // 6th mod
166 { "MOD4", GDK_MOD4_MASK }, // 7th mod
167 { "MOD5", GDK_MOD5_MASK }, // 8th mod
168 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
169 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
170 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
171 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
172 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
173 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
174 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
175 { "META", GDK_META_MASK }, // meta (since 2.10)
180 /* construct a hash from the var_name_to_ptr array for quick access */
182 make_var_to_name_hash() {
183 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
185 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
190 /* --- UTILITY FUNCTIONS --- */
192 expand_vars(char *s) {
195 char ret[256], *vend;
196 GString *buf = g_string_new("");
201 g_string_append_c(buf, *++s);
209 if( (vend = strchr(s, upto)) ||
210 (vend = strchr(s, '\0')) ) {
211 strncpy(ret, s, vend-s);
213 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
214 if(c->type == TYPE_STR)
215 g_string_append(buf, (gchar *)*c->ptr);
216 else if(c->type == TYPE_INT) {
217 char *b = itos((int)*c->ptr);
218 g_string_append(buf, b);
222 if(upto == ' ') s = vend;
228 g_string_append_c(buf, *s);
233 return g_string_free(buf, FALSE);
240 snprintf(tmp, sizeof(tmp), "%i", val);
241 return g_strdup(tmp);
245 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
248 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
251 str_replace (const char* search, const char* replace, const char* string) {
255 buf = g_strsplit (string, search, -1);
256 ret = g_strjoinv (replace, buf);
257 g_strfreev(buf); // somebody said this segfaults
263 read_file_by_line (gchar *path) {
264 GIOChannel *chan = NULL;
265 gchar *readbuf = NULL;
267 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
270 chan = g_io_channel_new_file(path, "r", NULL);
273 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
274 const gchar* val = g_strdup (readbuf);
275 g_array_append_val (lines, val);
280 g_io_channel_unref (chan);
282 fprintf(stderr, "File '%s' not be read.\n", path);
289 gchar* parseenv (char* string) {
290 extern char** environ;
291 gchar* tmpstr = NULL;
295 while (environ[i] != NULL) {
296 gchar** env = g_strsplit (environ[i], "=", 2);
297 gchar* envname = g_strconcat ("$", env[0], NULL);
299 if (g_strrstr (string, envname) != NULL) {
300 tmpstr = g_strdup(string);
302 string = str_replace(envname, env[1], tmpstr);
307 g_strfreev (env); // somebody said this breaks uzbl
315 setup_signal(int signr, sigfunc *shandler) {
316 struct sigaction nh, oh;
318 nh.sa_handler = shandler;
319 sigemptyset(&nh.sa_mask);
322 if(sigaction(signr, &nh, &oh) < 0)
330 if (uzbl.behave.fifo_dir)
331 unlink (uzbl.comm.fifo_path);
332 if (uzbl.behave.socket_dir)
333 unlink (uzbl.comm.socket_path);
335 g_free(uzbl.state.executable_path);
336 g_string_free(uzbl.state.keycmd, TRUE);
337 g_hash_table_destroy(uzbl.bindings);
338 g_hash_table_destroy(uzbl.behave.commands);
341 /* used for html_mode_timeout
342 * be sure to extend this function to use
343 * more timers if needed in other places
346 set_timeout(int seconds) {
348 memset(&t, 0, sizeof t);
350 t.it_value.tv_sec = seconds;
351 t.it_value.tv_usec = 0;
352 setitimer(ITIMER_REAL, &t, NULL);
355 /* --- SIGNAL HANDLER --- */
358 catch_sigterm(int s) {
364 catch_sigint(int s) {
374 set_var_value("mode", "0");
379 /* --- CALLBACKS --- */
382 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
385 (void) navigation_action;
386 (void) policy_decision;
388 const gchar* uri = webkit_network_request_get_uri (request);
389 if (uzbl.state.verbose)
390 printf("New window requested -> %s \n", uri);
391 new_window_load_uri(uri);
396 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
401 /* If we can display it, let's display it... */
402 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
403 webkit_web_policy_decision_use (policy_decision);
407 /* ...everything we can't displayed is downloaded */
408 webkit_web_policy_decision_download (policy_decision);
413 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
417 if (uzbl.state.selected_url != NULL) {
418 if (uzbl.state.verbose)
419 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
420 new_window_load_uri(uzbl.state.selected_url);
422 if (uzbl.state.verbose)
423 printf("New web view -> %s\n","Nothing to open, exiting");
429 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
432 if (uzbl.behave.download_handler) {
433 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
434 if (uzbl.state.verbose)
435 printf("Download -> %s\n",uri);
436 /* if urls not escaped, we may have to escape and quote uri before this call */
437 run_handler(uzbl.behave.download_handler, uri);
442 /* scroll a bar in a given direction */
444 scroll (GtkAdjustment* bar, GArray *argv) {
448 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
449 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
450 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
454 scroll_begin(WebKitWebView* page, GArray *argv) {
455 (void) page; (void) argv;
456 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
460 scroll_end(WebKitWebView* page, GArray *argv) {
461 (void) page; (void) argv;
462 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
463 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
467 scroll_vert(WebKitWebView* page, GArray *argv) {
469 scroll(uzbl.gui.bar_v, argv);
473 scroll_horz(WebKitWebView* page, GArray *argv) {
475 scroll(uzbl.gui.bar_h, argv);
480 if (!uzbl.behave.show_status) {
481 gtk_widget_hide(uzbl.gui.mainbar);
483 gtk_widget_show(uzbl.gui.mainbar);
489 toggle_status_cb (WebKitWebView* page, GArray *argv) {
493 if (uzbl.behave.show_status) {
494 gtk_widget_hide(uzbl.gui.mainbar);
496 gtk_widget_show(uzbl.gui.mainbar);
498 uzbl.behave.show_status = !uzbl.behave.show_status;
503 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
507 //Set selected_url state variable
508 g_free(uzbl.state.selected_url);
509 uzbl.state.selected_url = NULL;
511 uzbl.state.selected_url = g_strdup(link);
517 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
521 if (uzbl.gui.main_title)
522 g_free (uzbl.gui.main_title);
523 uzbl.gui.main_title = g_strdup (title);
528 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
531 uzbl.gui.sbar.load_progress = progress;
536 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
540 if (uzbl.behave.load_finish_handler)
541 run_handler(uzbl.behave.load_finish_handler, "");
545 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
549 uzbl.gui.sbar.load_progress = 0;
550 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
551 if (uzbl.behave.load_start_handler)
552 run_handler(uzbl.behave.load_start_handler, "");
556 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
559 g_free (uzbl.state.uri);
560 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
561 uzbl.state.uri = g_string_free (newuri, FALSE);
562 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
563 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
566 if (uzbl.behave.load_commit_handler)
567 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
571 destroy_cb (GtkWidget* widget, gpointer data) {
579 if (uzbl.behave.history_handler) {
581 struct tm * timeinfo;
584 timeinfo = localtime ( &rawtime );
585 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
586 run_handler(uzbl.behave.history_handler, date);
591 /* VIEW funcs (little webkit wrappers) */
592 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
594 VIEWFUNC(reload_bypass_cache)
595 VIEWFUNC(stop_loading)
602 /* -- command to callback/function map for things we cannot attach to any signals */
603 static struct {char *name; Command command[2];} cmdlist[] =
604 { /* key function no_split */
605 { "back", {view_go_back, 0} },
606 { "forward", {view_go_forward, 0} },
607 { "scroll_vert", {scroll_vert, 0} },
608 { "scroll_horz", {scroll_horz, 0} },
609 { "scroll_begin", {scroll_begin, 0} },
610 { "scroll_end", {scroll_end, 0} },
611 { "reload", {view_reload, 0}, },
612 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
613 { "stop", {view_stop_loading, 0}, },
614 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
615 { "zoom_out", {view_zoom_out, 0}, },
616 { "reset_zoom", {reset_zoom_level, 0}, },
617 { "uri", {load_uri, NOSPLIT} },
618 { "js", {run_js, NOSPLIT} },
619 { "script", {run_external_js, 0} },
620 { "toggle_status", {toggle_status_cb, 0} },
621 { "spawn", {spawn, 0} },
622 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
623 { "sh", {spawn_sh, 0} },
624 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
625 { "exit", {close_uzbl, 0} },
626 { "search", {search_forward_text, NOSPLIT} },
627 { "search_reverse", {search_reverse_text, NOSPLIT} },
628 { "dehilight", {dehilight, 0} },
629 { "toggle_insert_mode", {toggle_insert_mode, 0} },
630 { "set", {set_var, NOSPLIT} },
631 //{ "get", {get_var, NOSPLIT} },
632 { "bind", {act_bind, NOSPLIT} },
633 { "dump_config", {act_dump_config, 0} },
634 { "keycmd", {keycmd, NOSPLIT} },
635 { "keycmd_nl", {keycmd_nl, NOSPLIT} },
636 { "keycmd_bs", {keycmd_bs, 0} },
637 { "chain", {chain, 0} },
638 { "print", {print, NOSPLIT} }
645 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
647 for (i = 0; i < LENGTH(cmdlist); i++)
648 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
651 /* -- CORE FUNCTIONS -- */
654 free_action(gpointer act) {
655 Action *action = (Action*)act;
656 g_free(action->name);
658 g_free(action->param);
663 new_action(const gchar *name, const gchar *param) {
664 Action *action = g_new(Action, 1);
666 action->name = g_strdup(name);
668 action->param = g_strdup(param);
670 action->param = NULL;
676 file_exists (const char * filename) {
677 return (access(filename, F_OK) == 0);
681 set_var(WebKitWebView *page, GArray *argv) {
683 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
684 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
685 set_var_value(g_strstrip(split[0]), value);
691 print(WebKitWebView *page, GArray *argv) {
695 buf = expand_vars(argv_idx(argv, 0));
701 act_bind(WebKitWebView *page, GArray *argv) {
703 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
704 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
705 add_binding(g_strstrip(split[0]), value);
717 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
720 if (argv_idx(argv, 0)) {
721 if (strcmp (argv_idx(argv, 0), "0") == 0) {
722 uzbl.behave.insert_mode = FALSE;
724 uzbl.behave.insert_mode = TRUE;
727 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
734 load_uri (WebKitWebView *web_view, GArray *argv) {
735 if (argv_idx(argv, 0)) {
736 GString* newuri = g_string_new (argv_idx(argv, 0));
737 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
738 run_js(web_view, argv);
741 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
742 g_string_prepend (newuri, "http://");
743 /* if we do handle cookies, ask our handler for them */
744 webkit_web_view_load_uri (web_view, newuri->str);
745 g_string_free (newuri, TRUE);
750 run_js (WebKitWebView * web_view, GArray *argv) {
751 if (argv_idx(argv, 0))
752 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
756 run_external_js (WebKitWebView * web_view, GArray *argv) {
757 if (argv_idx(argv, 0)) {
758 GArray* lines = read_file_by_line (argv_idx (argv, 0));
763 while ((line = g_array_index(lines, gchar*, i))) {
765 js = g_strdup (line);
767 gchar* newjs = g_strconcat (js, line, NULL);
774 if (uzbl.state.verbose)
775 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
777 if (argv_idx (argv, 1)) {
778 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
782 webkit_web_view_execute_script (web_view, js);
784 g_array_free (lines, TRUE);
789 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
790 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
791 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
792 webkit_web_view_unmark_text_matches (page);
793 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
794 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
798 if (uzbl.state.searchtx) {
799 if (uzbl.state.verbose)
800 printf ("Searching: %s\n", uzbl.state.searchtx);
801 webkit_web_view_set_highlight_text_matches (page, TRUE);
802 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
807 search_forward_text (WebKitWebView *page, GArray *argv) {
808 search_text(page, argv, TRUE);
812 search_reverse_text (WebKitWebView *page, GArray *argv) {
813 search_text(page, argv, FALSE);
817 reset_zoom_level (WebKitWebView *page, GArray *argv) {
819 webkit_web_view_set_zoom_level (page, 1.0);
823 dehilight (WebKitWebView *page, GArray *argv) {
825 webkit_web_view_set_highlight_text_matches (page, FALSE);
830 new_window_load_uri (const gchar * uri) {
831 GString* to_execute = g_string_new ("");
832 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
834 for (i = 0; entries[i].long_name != NULL; i++) {
835 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
836 gchar** str = (gchar**)entries[i].arg_data;
838 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
842 if (uzbl.state.verbose)
843 printf("\n%s\n", to_execute->str);
844 g_spawn_command_line_async (to_execute->str, NULL);
845 g_string_free (to_execute, TRUE);
849 chain (WebKitWebView *page, GArray *argv) {
852 gchar **parts = NULL;
854 while ((a = argv_idx(argv, i++))) {
855 parts = g_strsplit (a, " ", 2);
856 parse_command(parts[0], parts[1]);
862 keycmd (WebKitWebView *page, GArray *argv) {
865 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
871 keycmd_nl (WebKitWebView *page, GArray *argv) {
874 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
880 keycmd_bs (WebKitWebView *page, GArray *argv) {
883 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
888 close_uzbl (WebKitWebView *page, GArray *argv) {
894 /* --Statusbar functions-- */
896 build_progressbar_ascii(int percent) {
897 int width=uzbl.gui.sbar.progress_w;
900 GString *bar = g_string_new("");
902 l = (double)percent*((double)width/100.);
903 l = (int)(l+.5)>=(int)l ? l+.5 : l;
905 for(i=0; i<(int)l; i++)
906 g_string_append(bar, uzbl.gui.sbar.progress_s);
909 g_string_append(bar, uzbl.gui.sbar.progress_u);
911 return g_string_free(bar, FALSE);
916 const GScannerConfig scan_config = {
919 ) /* cset_skip_characters */,
924 ) /* cset_identifier_first */,
931 ) /* cset_identifier_nth */,
932 ( "" ) /* cpair_comment_single */,
934 TRUE /* case_sensitive */,
936 FALSE /* skip_comment_multi */,
937 FALSE /* skip_comment_single */,
938 FALSE /* scan_comment_multi */,
939 TRUE /* scan_identifier */,
940 TRUE /* scan_identifier_1char */,
941 FALSE /* scan_identifier_NULL */,
942 TRUE /* scan_symbols */,
943 FALSE /* scan_binary */,
944 FALSE /* scan_octal */,
945 FALSE /* scan_float */,
946 FALSE /* scan_hex */,
947 FALSE /* scan_hex_dollar */,
948 FALSE /* scan_string_sq */,
949 FALSE /* scan_string_dq */,
950 TRUE /* numbers_2_int */,
951 FALSE /* int_2_float */,
952 FALSE /* identifier_2_string */,
953 FALSE /* char_2_token */,
954 FALSE /* symbol_2_token */,
955 TRUE /* scope_0_fallback */,
960 uzbl.scan = g_scanner_new(&scan_config);
961 while(symp->symbol_name) {
962 g_scanner_scope_add_symbol(uzbl.scan, 0,
964 GINT_TO_POINTER(symp->symbol_token));
970 expand_template(const char *template, gboolean escape_markup) {
971 if(!template) return NULL;
973 GTokenType token = G_TOKEN_NONE;
974 GString *ret = g_string_new("");
978 g_scanner_input_text(uzbl.scan, template, strlen(template));
979 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
980 token = g_scanner_get_next_token(uzbl.scan);
982 if(token == G_TOKEN_SYMBOL) {
983 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
987 buf = uzbl.state.uri?
988 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
989 g_string_append(ret, buf);
993 g_string_append(ret, uzbl.state.uri?
994 uzbl.state.uri:g_strdup(""));
997 buf = itos(uzbl.gui.sbar.load_progress);
998 g_string_append(ret, buf);
1001 case SYM_LOADPRGSBAR:
1002 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1003 g_string_append(ret, buf);
1008 buf = uzbl.gui.main_title?
1009 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1010 g_string_append(ret, buf);
1014 g_string_append(ret, uzbl.gui.main_title?
1015 uzbl.gui.main_title:g_strdup(""));
1017 case SYM_SELECTED_URI:
1019 buf = uzbl.state.selected_url?
1020 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1021 g_string_append(ret, buf);
1025 g_string_append(ret, uzbl.state.selected_url?
1026 uzbl.state.selected_url:g_strdup(""));
1029 buf = itos(uzbl.xwin);
1030 g_string_append(ret,
1031 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1036 buf = uzbl.state.keycmd->str?
1037 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1038 g_string_append(ret, buf);
1042 g_string_append(ret, uzbl.state.keycmd->str?
1043 uzbl.state.keycmd->str:g_strdup(""));
1046 g_string_append(ret,
1047 uzbl.behave.insert_mode?
1048 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1051 g_string_append(ret,
1052 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1054 /* useragent syms */
1056 buf = itos(WEBKIT_MAJOR_VERSION);
1057 g_string_append(ret, buf);
1061 buf = itos(WEBKIT_MINOR_VERSION);
1062 g_string_append(ret, buf);
1066 buf = itos(WEBKIT_MICRO_VERSION);
1067 g_string_append(ret, buf);
1071 g_string_append(ret, uzbl.state.unameinfo.sysname);
1074 g_string_append(ret, uzbl.state.unameinfo.nodename);
1077 g_string_append(ret, uzbl.state.unameinfo.release);
1080 g_string_append(ret, uzbl.state.unameinfo.version);
1083 g_string_append(ret, uzbl.state.unameinfo.machine);
1086 g_string_append(ret, ARCH);
1089 case SYM_DOMAINNAME:
1090 g_string_append(ret, uzbl.state.unameinfo.domainname);
1094 g_string_append(ret, COMMIT);
1100 else if(token == G_TOKEN_INT) {
1101 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1102 g_string_append(ret, buf);
1105 else if(token == G_TOKEN_IDENTIFIER) {
1106 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1108 else if(token == G_TOKEN_CHAR) {
1109 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1113 return g_string_free(ret, FALSE);
1115 /* --End Statusbar functions-- */
1118 sharg_append(GArray *a, const gchar *str) {
1119 const gchar *s = (str ? str : "");
1120 g_array_append_val(a, s);
1123 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1125 run_command (const gchar *command, const guint npre, const gchar **args,
1126 const gboolean sync, char **output_stdout) {
1127 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1130 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1131 gchar *pid = itos(getpid());
1132 gchar *xwin = itos(uzbl.xwin);
1134 sharg_append(a, command);
1135 for (i = 0; i < npre; i++) /* add n args before the default vars */
1136 sharg_append(a, args[i]);
1137 sharg_append(a, uzbl.state.config_file);
1138 sharg_append(a, pid);
1139 sharg_append(a, xwin);
1140 sharg_append(a, uzbl.comm.fifo_path);
1141 sharg_append(a, uzbl.comm.socket_path);
1142 sharg_append(a, uzbl.state.uri);
1143 sharg_append(a, uzbl.gui.main_title);
1145 for (i = npre; i < g_strv_length((gchar**)args); i++)
1146 sharg_append(a, args[i]);
1150 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1152 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1153 NULL, NULL, output_stdout, NULL, NULL, &err);
1154 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1155 NULL, NULL, NULL, &err);
1157 if (uzbl.state.verbose) {
1158 GString *s = g_string_new("spawned:");
1159 for (i = 0; i < (a->len); i++) {
1160 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1161 g_string_append_printf(s, " %s", qarg);
1164 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1165 printf("%s\n", s->str);
1166 g_string_free(s, TRUE);
1168 printf("Stdout: %s\n", *output_stdout);
1172 g_printerr("error on run_command: %s\n", err->message);
1177 g_array_free (a, TRUE);
1182 split_quoted(const gchar* src, const gboolean unquote) {
1183 /* split on unquoted space, return array of strings;
1184 remove a layer of quotes and backslashes if unquote */
1185 if (!src) return NULL;
1187 gboolean dq = FALSE;
1188 gboolean sq = FALSE;
1189 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1190 GString *s = g_string_new ("");
1194 for (p = src; *p != '\0'; p++) {
1195 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1196 else if (*p == '\\') { g_string_append_c(s, *p++);
1197 g_string_append_c(s, *p); }
1198 else if ((*p == '"') && unquote && !sq) dq = !dq;
1199 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1201 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1202 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1204 else if ((*p == ' ') && !dq && !sq) {
1205 dup = g_strdup(s->str);
1206 g_array_append_val(a, dup);
1207 g_string_truncate(s, 0);
1208 } else g_string_append_c(s, *p);
1210 dup = g_strdup(s->str);
1211 g_array_append_val(a, dup);
1212 ret = (gchar**)a->data;
1213 g_array_free (a, FALSE);
1214 g_string_free (s, TRUE);
1219 spawn(WebKitWebView *web_view, GArray *argv) {
1221 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1222 if (argv_idx(argv, 0))
1223 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1227 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1230 if (argv_idx(argv, 0))
1231 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1232 TRUE, &uzbl.comm.sync_stdout);
1236 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1238 if (!uzbl.behave.shell_cmd) {
1239 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1244 gchar *spacer = g_strdup("");
1245 g_array_insert_val(argv, 1, spacer);
1246 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1248 for (i = 1; i < g_strv_length(cmd); i++)
1249 g_array_prepend_val(argv, cmd[i]);
1251 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1257 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1259 if (!uzbl.behave.shell_cmd) {
1260 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1265 gchar *spacer = g_strdup("");
1266 g_array_insert_val(argv, 1, spacer);
1267 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1269 for (i = 1; i < g_strv_length(cmd); i++)
1270 g_array_prepend_val(argv, cmd[i]);
1272 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1273 TRUE, &uzbl.comm.sync_stdout);
1279 parse_command(const char *cmd, const char *param) {
1282 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1285 gchar **par = split_quoted(param, TRUE);
1286 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1288 if (c[1] == NOSPLIT) { /* don't split */
1289 sharg_append(a, param);
1291 for (i = 0; i < g_strv_length(par); i++)
1292 sharg_append(a, par[i]);
1294 c[0](uzbl.gui.web_view, a);
1296 g_array_free (a, TRUE);
1299 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1306 if(*uzbl.net.proxy_url == ' '
1307 || uzbl.net.proxy_url == NULL) {
1308 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1309 (GType) SOUP_SESSION_PROXY_URI);
1312 suri = soup_uri_new(uzbl.net.proxy_url);
1313 g_object_set(G_OBJECT(uzbl.net.soup_session),
1314 SOUP_SESSION_PROXY_URI,
1316 soup_uri_free(suri);
1323 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1324 g_array_append_val (a, uzbl.state.uri);
1325 load_uri(uzbl.gui.web_view, a);
1326 g_array_free (a, TRUE);
1330 cmd_always_insert_mode() {
1331 uzbl.behave.insert_mode =
1332 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1338 g_object_set(G_OBJECT(uzbl.net.soup_session),
1339 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1343 cmd_max_conns_host() {
1344 g_object_set(G_OBJECT(uzbl.net.soup_session),
1345 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1350 soup_session_remove_feature
1351 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1352 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1353 /*g_free(uzbl.net.soup_logger);*/
1355 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1356 soup_session_add_feature(uzbl.net.soup_session,
1357 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1360 static WebKitWebSettings*
1362 return webkit_web_view_get_settings(uzbl.gui.web_view);
1367 WebKitWebSettings *ws = view_settings();
1368 if (uzbl.behave.font_size > 0) {
1369 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1372 if (uzbl.behave.monospace_size > 0) {
1373 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1374 uzbl.behave.monospace_size, NULL);
1376 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1377 uzbl.behave.font_size, NULL);
1383 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1387 cmd_disable_plugins() {
1388 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1389 !uzbl.behave.disable_plugins, NULL);
1393 cmd_disable_scripts() {
1394 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1395 !uzbl.behave.disable_scripts, NULL);
1399 cmd_minimum_font_size() {
1400 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1401 uzbl.behave.minimum_font_size, NULL);
1404 cmd_autoload_img() {
1405 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1406 uzbl.behave.autoload_img, NULL);
1411 cmd_autoshrink_img() {
1412 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1413 uzbl.behave.autoshrink_img, NULL);
1418 cmd_enable_spellcheck() {
1419 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1420 uzbl.behave.enable_spellcheck, NULL);
1424 cmd_enable_private() {
1425 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1426 uzbl.behave.enable_private, NULL);
1431 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1432 uzbl.behave.print_bg, NULL);
1437 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1438 uzbl.behave.style_uri, NULL);
1442 cmd_resizable_txt() {
1443 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1444 uzbl.behave.resizable_txt, NULL);
1448 cmd_default_encoding() {
1449 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1450 uzbl.behave.default_encoding, NULL);
1454 cmd_enforce_96dpi() {
1455 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1456 uzbl.behave.enforce_96dpi, NULL);
1460 cmd_caret_browsing() {
1461 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1462 uzbl.behave.caret_browsing, NULL);
1466 cmd_cookie_handler() {
1467 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1468 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1469 if ((g_strcmp0(split[0], "sh") == 0) ||
1470 (g_strcmp0(split[0], "spawn") == 0)) {
1471 g_free (uzbl.behave.cookie_handler);
1472 uzbl.behave.cookie_handler =
1473 g_strdup_printf("sync_%s %s", split[0], split[1]);
1480 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1485 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1490 if(uzbl.behave.inject_html) {
1491 webkit_web_view_load_html_string (uzbl.gui.web_view,
1492 uzbl.behave.inject_html, NULL);
1501 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1502 uzbl.behave.modmask = 0;
1504 if(uzbl.behave.modkey)
1505 g_free(uzbl.behave.modkey);
1506 uzbl.behave.modkey = buf;
1508 for (i = 0; modkeys[i].key != NULL; i++) {
1509 if (g_strrstr(buf, modkeys[i].key))
1510 uzbl.behave.modmask |= modkeys[i].mask;
1516 if (*uzbl.net.useragent == ' ') {
1517 g_free (uzbl.net.useragent);
1518 uzbl.net.useragent = NULL;
1520 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1522 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1523 g_free(uzbl.net.useragent);
1524 uzbl.net.useragent = ua;
1530 gtk_widget_ref(uzbl.gui.scrolled_win);
1531 gtk_widget_ref(uzbl.gui.mainbar);
1532 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1533 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1535 if(uzbl.behave.status_top) {
1536 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1537 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1540 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1541 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1543 gtk_widget_unref(uzbl.gui.scrolled_win);
1544 gtk_widget_unref(uzbl.gui.mainbar);
1545 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1550 set_var_value(gchar *name, gchar *val) {
1551 uzbl_cmdprop *c = NULL;
1555 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1556 /* check for the variable type */
1557 if (c->type == TYPE_STR) {
1558 buf = expand_vars(val);
1561 } else if(c->type == TYPE_INT) {
1562 int *ip = (int *)c->ptr;
1563 buf = expand_vars(val);
1564 *ip = (int)strtoul(buf, &endp, 10);
1566 } else if (c->type == TYPE_FLOAT) {
1567 float *fp = (float *)c->ptr;
1568 buf = expand_vars(val);
1569 *fp = strtod(buf, &endp);
1573 /* invoke a command specific function */
1574 if(c->func) c->func();
1581 Behaviour *b = &uzbl.behave;
1583 if(b->html_buffer->str) {
1584 webkit_web_view_load_html_string (uzbl.gui.web_view,
1585 b->html_buffer->str, b->base_url);
1586 g_string_free(b->html_buffer, TRUE);
1587 b->html_buffer = g_string_new("");
1591 enum {M_CMD, M_HTML};
1593 parse_cmd_line(const char *ctl_line) {
1594 Behaviour *b = &uzbl.behave;
1597 if(b->mode == M_HTML) {
1598 len = strlen(b->html_endmarker);
1599 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1600 if(len == strlen(ctl_line)-1 &&
1601 !strncmp(b->html_endmarker, ctl_line, len)) {
1603 set_var_value("mode", "0");
1608 set_timeout(b->html_timeout);
1609 g_string_append(b->html_buffer, ctl_line);
1612 else if((ctl_line[0] == '#') /* Comments */
1613 || (ctl_line[0] == ' ')
1614 || (ctl_line[0] == '\n'))
1615 ; /* ignore these lines */
1616 else { /* parse a command */
1618 gchar **tokens = NULL;
1619 len = strlen(ctl_line);
1621 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1622 ctlstrip = g_strndup(ctl_line, len - 1);
1623 else ctlstrip = g_strdup(ctl_line);
1625 tokens = g_strsplit(ctlstrip, " ", 2);
1626 parse_command(tokens[0], tokens[1]);
1633 build_stream_name(int type, const gchar* dir) {
1635 State *s = &uzbl.state;
1638 xwin_str = itos((int)uzbl.xwin);
1640 str = g_strdup_printf
1641 ("%s/uzbl_fifo_%s", dir,
1642 s->instance_name ? s->instance_name : xwin_str);
1643 } else if (type == SOCKET) {
1644 str = g_strdup_printf
1645 ("%s/uzbl_socket_%s", dir,
1646 s->instance_name ? s->instance_name : xwin_str );
1653 control_fifo(GIOChannel *gio, GIOCondition condition) {
1654 if (uzbl.state.verbose)
1655 printf("triggered\n");
1660 if (condition & G_IO_HUP)
1661 g_error ("Fifo: Read end of pipe died!\n");
1664 g_error ("Fifo: GIOChannel broke\n");
1666 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1667 if (ret == G_IO_STATUS_ERROR) {
1668 g_error ("Fifo: Error reading: %s\n", err->message);
1672 parse_cmd_line(ctl_line);
1679 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1680 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1681 if (unlink(uzbl.comm.fifo_path) == -1)
1682 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1683 g_free(uzbl.comm.fifo_path);
1684 uzbl.comm.fifo_path = NULL;
1687 if (*dir == ' ') { /* space unsets the variable */
1692 GIOChannel *chan = NULL;
1693 GError *error = NULL;
1694 gchar *path = build_stream_name(FIFO, dir);
1696 // This extra check is an alternative to recursively calling init_fifo() or using goto
1697 if (file_exists(path)) {
1699 g_warning ("init_fifo: can't delete %s: %s", path, strerror(errno));
1702 if (!file_exists(path)) {
1703 if (mkfifo (path, 0666) == 0) {
1704 // 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.
1705 chan = g_io_channel_new_file(path, "r+", &error);
1707 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1708 if (uzbl.state.verbose)
1709 printf ("init_fifo: created successfully as %s\n", path);
1710 uzbl.comm.fifo_path = path;
1712 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1713 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1714 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1715 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1717 /* if we got this far, there was an error; cleanup */
1718 if (error) g_error_free (error);
1725 control_stdin(GIOChannel *gio, GIOCondition condition) {
1727 gchar *ctl_line = NULL;
1730 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1731 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1734 parse_cmd_line(ctl_line);
1742 GIOChannel *chan = NULL;
1743 GError *error = NULL;
1745 chan = g_io_channel_unix_new(fileno(stdin));
1747 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1748 g_error ("Stdin: could not add watch\n");
1750 if (uzbl.state.verbose)
1751 printf ("Stdin: watch added successfully\n");
1754 g_error ("Stdin: Error while opening: %s\n", error->message);
1756 if (error) g_error_free (error);
1760 control_socket(GIOChannel *chan) {
1761 struct sockaddr_un remote;
1762 char buffer[512], *ctl_line;
1764 int sock, clientsock, n, done;
1767 sock = g_io_channel_unix_get_fd(chan);
1769 memset (buffer, 0, sizeof (buffer));
1771 t = sizeof (remote);
1772 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1776 memset (temp, 0, sizeof (temp));
1777 n = recv (clientsock, temp, 128, 0);
1779 buffer[strlen (buffer)] = '\0';
1783 strcat (buffer, temp);
1786 if (strcmp (buffer, "\n") < 0) {
1787 buffer[strlen (buffer) - 1] = '\0';
1789 buffer[strlen (buffer)] = '\0';
1792 ctl_line = g_strdup(buffer);
1793 parse_cmd_line (ctl_line);
1796 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1797 GError *error = NULL;
1800 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1801 if (ret == G_IO_STATUS_ERROR)
1802 g_error ("Error reading: %s\n", error->message);
1804 printf("Got line %s (%u bytes) \n",ctl_line, len);
1806 parse_line(ctl_line);
1814 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1815 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1816 if (unlink(uzbl.comm.socket_path) == -1)
1817 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1818 g_free(uzbl.comm.socket_path);
1819 uzbl.comm.socket_path = NULL;
1827 GIOChannel *chan = NULL;
1829 struct sockaddr_un local;
1830 gchar *path = build_stream_name(SOCKET, dir);
1832 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1834 local.sun_family = AF_UNIX;
1835 strcpy (local.sun_path, path);
1836 unlink (local.sun_path);
1838 len = strlen (local.sun_path) + sizeof (local.sun_family);
1839 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1840 if (uzbl.state.verbose)
1841 printf ("init_socket: opened in %s\n", path);
1844 if( (chan = g_io_channel_unix_new(sock)) ) {
1845 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1846 uzbl.comm.socket_path = path;
1849 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1851 /* if we got this far, there was an error; cleanup */
1858 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1859 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1861 // this function may be called very early when the templates are not set (yet), hence the checks
1863 update_title (void) {
1864 Behaviour *b = &uzbl.behave;
1867 if (b->show_status) {
1868 if (b->title_format_short) {
1869 parsed = expand_template(b->title_format_short, FALSE);
1870 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1873 if (b->status_format) {
1874 parsed = expand_template(b->status_format, TRUE);
1875 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1878 if (b->status_background) {
1880 gdk_color_parse (b->status_background, &color);
1881 //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)
1882 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1885 if (b->title_format_long) {
1886 parsed = expand_template(b->title_format_long, FALSE);
1887 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1894 key_press_cb (GtkWidget* window, GdkEventKey* event)
1896 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1900 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1901 || 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)
1904 /* turn off insert mode (if always_insert_mode is not used) */
1905 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1906 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1911 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1914 if (event->keyval == GDK_Escape) {
1915 g_string_truncate(uzbl.state.keycmd, 0);
1917 dehilight(uzbl.gui.web_view, NULL);
1921 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1922 if (event->keyval == GDK_Insert) {
1924 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1925 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1927 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1930 g_string_append (uzbl.state.keycmd, str);
1937 if (event->keyval == GDK_BackSpace)
1938 keycmd_bs(NULL, NULL);
1940 gboolean key_ret = FALSE;
1941 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1943 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1945 run_keycmd(key_ret);
1947 if (key_ret) return (!uzbl.behave.insert_mode);
1952 run_keycmd(const gboolean key_ret) {
1953 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1955 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1956 g_string_truncate(uzbl.state.keycmd, 0);
1957 parse_command(act->name, act->param);
1961 /* try if it's an incremental keycmd or one that takes args, and run it */
1962 GString* short_keys = g_string_new ("");
1963 GString* short_keys_inc = g_string_new ("");
1965 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1966 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1967 g_string_assign(short_keys_inc, short_keys->str);
1968 g_string_append_c(short_keys, '_');
1969 g_string_append_c(short_keys_inc, '*');
1971 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1972 /* run normal cmds only if return was pressed */
1973 exec_paramcmd(act, i);
1974 g_string_truncate(uzbl.state.keycmd, 0);
1976 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1977 if (key_ret) /* just quit the incremental command on return */
1978 g_string_truncate(uzbl.state.keycmd, 0);
1979 else exec_paramcmd(act, i); /* otherwise execute the incremental */
1983 g_string_truncate(short_keys, short_keys->len - 1);
1985 g_string_free (short_keys, TRUE);
1986 g_string_free (short_keys_inc, TRUE);
1990 exec_paramcmd(const Action *act, const guint i) {
1991 GString *parampart = g_string_new (uzbl.state.keycmd->str);
1992 GString *actionname = g_string_new ("");
1993 GString *actionparam = g_string_new ("");
1994 g_string_erase (parampart, 0, i+1);
1996 g_string_printf (actionname, act->name, parampart->str);
1998 g_string_printf (actionparam, act->param, parampart->str);
1999 parse_command(actionname->str, actionparam->str);
2000 g_string_free(actionname, TRUE);
2001 g_string_free(actionparam, TRUE);
2002 g_string_free(parampart, TRUE);
2010 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2011 //main_window_ref = g_object_ref(scrolled_window);
2012 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
2014 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2015 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2017 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2018 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2019 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2020 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2021 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2022 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2023 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2024 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2025 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2026 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2027 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2029 return scrolled_window;
2036 g->mainbar = gtk_hbox_new (FALSE, 0);
2038 /* keep a reference to the bar so we can re-pack it at runtime*/
2039 //sbar_ref = g_object_ref(g->mainbar);
2041 g->mainbar_label = gtk_label_new ("");
2042 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2043 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2044 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2045 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2046 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2047 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2052 GtkWidget* create_window () {
2053 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2054 gchar* uzbl_icon = find_xdg_file(1, "/uzbl/uzbl.png");
2055 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2056 gtk_widget_set_name (window, "Uzbl browser");
2057 gtk_window_set_icon_from_file (GTK_WINDOW (window), uzbl_icon, NULL);
2058 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2059 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2067 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2069 If actname is one that calls an external command, this function will inject
2070 newargs in front of the user-provided args in that command line. They will
2071 come become after the body of the script (in sh) or after the name of
2072 the command to execute (in spawn).
2073 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2074 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2076 The return value consist of two strings: the action (sh, ...) and its args.
2078 If act is not one that calls an external command, then the given action merely
2081 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2082 gchar *actdup = g_strdup(actname);
2083 g_array_append_val(rets, actdup);
2085 if ((g_strcmp0(actname, "spawn") == 0) ||
2086 (g_strcmp0(actname, "sh") == 0) ||
2087 (g_strcmp0(actname, "sync_spawn") == 0) ||
2088 (g_strcmp0(actname, "sync_sh") == 0)) {
2090 GString *a = g_string_new("");
2091 gchar **spawnparts = split_quoted(origargs, FALSE);
2092 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2093 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2095 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2096 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2098 g_array_append_val(rets, a->str);
2099 g_string_free(a, FALSE);
2100 g_strfreev(spawnparts);
2102 gchar *origdup = g_strdup(origargs);
2103 g_array_append_val(rets, origdup);
2105 return (gchar**)g_array_free(rets, FALSE);
2109 run_handler (const gchar *act, const gchar *args) {
2110 /* Consider this code a temporary hack to make the handlers usable.
2111 In practice, all this splicing, injection, and reconstruction is
2112 inefficient, annoying and hard to manage. Potential pitfalls arise
2113 when the handler specific args 1) are not quoted (the handler
2114 callbacks should take care of this) 2) are quoted but interfere
2115 with the users' own quotation. A more ideal solution is
2116 to refactor parse_command so that it doesn't just take a string
2117 and execute it; rather than that, we should have a function which
2118 returns the argument vector parsed from the string. This vector
2119 could be modified (e.g. insert additional args into it) before
2120 passing it to the next function that actually executes it. Though
2121 it still isn't perfect for chain actions.. will reconsider & re-
2122 factor when I have the time. -duc */
2124 char **parts = g_strsplit(act, " ", 2);
2126 if (g_strcmp0(parts[0], "chain") == 0) {
2127 GString *newargs = g_string_new("");
2128 gchar **chainparts = split_quoted(parts[1], FALSE);
2130 /* for every argument in the chain, inject the handler args
2131 and make sure the new parts are wrapped in quotes */
2132 gchar **cp = chainparts;
2134 gchar *quotless = NULL;
2135 gchar **spliced_quotless = NULL; // sigh -_-;
2136 gchar **inpart = NULL;
2139 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2141 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2142 } else quotless = g_strdup(*cp);
2144 spliced_quotless = g_strsplit(quotless, " ", 2);
2145 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2146 g_strfreev(spliced_quotless);
2148 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2154 parse_command(parts[0], &(newargs->str[1]));
2155 g_string_free(newargs, TRUE);
2156 g_strfreev(chainparts);
2159 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2160 parse_command(inparts[0], inparts[1]);
2168 add_binding (const gchar *key, const gchar *act) {
2169 char **parts = g_strsplit(act, " ", 2);
2176 if (uzbl.state.verbose)
2177 printf ("Binding %-10s : %s\n", key, act);
2178 action = new_action(parts[0], parts[1]);
2180 if (g_hash_table_remove (uzbl.bindings, key))
2181 g_warning ("Overwriting existing binding for \"%s\"", key);
2182 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2187 get_xdg_var (XDG_Var xdg) {
2188 const gchar* actual_value = getenv (xdg.environmental);
2189 const gchar* home = getenv ("HOME");
2190 gchar* return_value;
2192 if (! actual_value || strcmp (actual_value, "") == 0) {
2193 if (xdg.default_value) {
2194 return_value = str_replace ("~", home, xdg.default_value);
2196 return_value = NULL;
2199 return_value = str_replace("~", home, actual_value);
2202 return return_value;
2206 find_xdg_file (int xdg_type, char* filename) {
2207 /* xdg_type = 0 => config
2208 xdg_type = 1 => data
2209 xdg_type = 2 => cache*/
2211 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2212 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2215 gchar* temporary_string;
2219 if (! file_exists (temporary_file) && xdg_type != 2) {
2220 buf = get_xdg_var (XDG[3 + xdg_type]);
2221 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2224 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2225 g_free (temporary_file);
2226 temporary_file = g_strconcat (temporary_string, filename, NULL);
2230 //g_free (temporary_string); - segfaults.
2232 if (file_exists (temporary_file)) {
2233 return temporary_file;
2240 State *s = &uzbl.state;
2241 Network *n = &uzbl.net;
2243 for (i = 0; default_config[i].command != NULL; i++) {
2244 parse_cmd_line(default_config[i].command);
2247 if (!s->config_file) {
2248 s->config_file = find_xdg_file (0, "/uzbl/config");
2251 if (s->config_file) {
2252 GArray* lines = read_file_by_line (s->config_file);
2256 while ((line = g_array_index(lines, gchar*, i))) {
2257 parse_cmd_line (line);
2261 g_array_free (lines, TRUE);
2263 if (uzbl.state.verbose)
2264 printf ("No configuration file loaded.\n");
2267 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2270 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2273 if (!uzbl.behave.cookie_handler)
2276 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2277 GString *s = g_string_new ("");
2278 SoupURI * soup_uri = soup_message_get_uri(msg);
2279 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2280 run_handler(uzbl.behave.cookie_handler, s->str);
2282 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2283 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2284 if ( p != NULL ) *p = '\0';
2285 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2287 if (uzbl.comm.sync_stdout)
2288 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2290 g_string_free(s, TRUE);
2294 save_cookies (SoupMessage *msg, gpointer user_data){
2298 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2299 cookie = soup_cookie_to_set_cookie_header(ck->data);
2300 SoupURI * soup_uri = soup_message_get_uri(msg);
2301 GString *s = g_string_new ("");
2302 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2303 run_handler(uzbl.behave.cookie_handler, s->str);
2305 g_string_free(s, TRUE);
2310 /* --- WEBINSPECTOR --- */
2312 hide_window_cb(GtkWidget *widget, gpointer data) {
2315 gtk_widget_hide(widget);
2318 static WebKitWebView*
2319 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2322 (void) web_inspector;
2323 GtkWidget* scrolled_window;
2324 GtkWidget* new_web_view;
2327 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2328 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2329 G_CALLBACK(hide_window_cb), NULL);
2331 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2332 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2333 gtk_widget_show(g->inspector_window);
2335 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2336 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2337 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2338 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2339 gtk_widget_show(scrolled_window);
2341 new_web_view = webkit_web_view_new();
2342 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2344 return WEBKIT_WEB_VIEW(new_web_view);
2348 inspector_show_window_cb (WebKitWebInspector* inspector){
2350 gtk_widget_show(uzbl.gui.inspector_window);
2354 /* TODO: Add variables and code to make use of these functions */
2356 inspector_close_window_cb (WebKitWebInspector* inspector){
2362 inspector_attach_window_cb (WebKitWebInspector* inspector){
2368 inspector_detach_window_cb (WebKitWebInspector* inspector){
2374 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2380 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2386 set_up_inspector() {
2388 WebKitWebSettings *settings = view_settings();
2389 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2391 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2392 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2393 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2394 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2395 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2396 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2397 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2399 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2403 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2405 uzbl_cmdprop *c = v;
2410 if(c->type == TYPE_STR)
2411 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2412 else if(c->type == TYPE_INT)
2413 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2417 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2421 printf("bind %s = %s %s\n", (char *)k ,
2422 (char *)a->name, a->param?(char *)a->param:"");
2427 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2428 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2433 main (int argc, char* argv[]) {
2434 gtk_init (&argc, &argv);
2435 if (!g_thread_supported ())
2436 g_thread_init (NULL);
2437 uzbl.state.executable_path = g_strdup(argv[0]);
2438 uzbl.state.selected_url = NULL;
2439 uzbl.state.searchtx = NULL;
2441 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2442 g_option_context_add_main_entries (context, entries, NULL);
2443 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2444 g_option_context_parse (context, &argc, &argv, NULL);
2445 g_option_context_free(context);
2447 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2448 gboolean verbose_override = uzbl.state.verbose;
2450 /* initialize hash table */
2451 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2453 uzbl.net.soup_session = webkit_get_default_session();
2454 uzbl.state.keycmd = g_string_new("");
2456 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2457 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2458 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2459 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2460 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2461 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2464 if(uname(&uzbl.state.unameinfo) == -1)
2465 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2467 uzbl.gui.sbar.progress_s = g_strdup("=");
2468 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2469 uzbl.gui.sbar.progress_w = 10;
2471 /* HTML mode defaults*/
2472 uzbl.behave.html_buffer = g_string_new("");
2473 uzbl.behave.html_endmarker = g_strdup(".");
2474 uzbl.behave.html_timeout = 60;
2475 uzbl.behave.base_url = g_strdup("http://invalid");
2477 /* default mode indicators */
2478 uzbl.behave.insert_indicator = g_strdup("I");
2479 uzbl.behave.cmd_indicator = g_strdup("C");
2483 make_var_to_name_hash();
2485 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2487 uzbl.gui.scrolled_win = create_browser();
2490 /* initial packing */
2491 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2492 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2494 uzbl.gui.main_window = create_window ();
2495 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2498 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2499 gtk_widget_show_all (uzbl.gui.main_window);
2500 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2502 if (uzbl.state.verbose) {
2503 printf("Uzbl start location: %s\n", argv[0]);
2504 printf("window_id %i\n",(int) uzbl.xwin);
2505 printf("pid %i\n", getpid ());
2506 printf("name: %s\n", uzbl.state.instance_name);
2509 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2510 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2511 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2512 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2513 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2517 if (!uzbl.behave.show_status)
2518 gtk_widget_hide(uzbl.gui.mainbar);
2527 if (verbose_override > uzbl.state.verbose)
2528 uzbl.state.verbose = verbose_override;
2531 set_var_value("uri", uri_override);
2532 g_free(uri_override);
2533 } else if (uzbl.state.uri)
2534 cmd_load_uri(uzbl.gui.web_view, NULL);
2539 return EXIT_SUCCESS;
2542 /* vi: set et ts=4: */