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 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
163 { "SHIFT", GDK_SHIFT_MASK }, // shift
164 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
165 { "CONTROL", GDK_CONTROL_MASK }, // control
166 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
167 { "MOD2", GDK_MOD2_MASK }, // 5th mod
168 { "MOD3", GDK_MOD3_MASK }, // 6th mod
169 { "MOD4", GDK_MOD4_MASK }, // 7th mod
170 { "MOD5", GDK_MOD5_MASK }, // 8th mod
171 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
177 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
178 { "META", GDK_META_MASK }, // meta (since 2.10)
183 /* construct a hash from the var_name_to_ptr array for quick access */
185 make_var_to_name_hash() {
186 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
188 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
193 /* --- UTILITY FUNCTIONS --- */
195 expand_vars(char *s) {
198 char ret[256], *vend;
199 GString *buf = g_string_new("");
204 g_string_append_c(buf, *++s);
212 if( (vend = strchr(s, upto)) ||
213 (vend = strchr(s, '\0')) ) {
214 strncpy(ret, s, vend-s);
216 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
217 if(c->type == TYPE_STR)
218 g_string_append(buf, (gchar *)*c->ptr);
219 else if(c->type == TYPE_INT) {
220 char *b = itos((int)*c->ptr);
221 g_string_append(buf, b);
225 if(upto == ' ') s = vend;
231 g_string_append_c(buf, *s);
236 return g_string_free(buf, FALSE);
243 snprintf(tmp, sizeof(tmp), "%i", val);
244 return g_strdup(tmp);
248 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
251 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
254 str_replace (const char* search, const char* replace, const char* string) {
258 buf = g_strsplit (string, search, -1);
259 ret = g_strjoinv (replace, buf);
260 g_strfreev(buf); // somebody said this segfaults
266 read_file_by_line (gchar *path) {
267 GIOChannel *chan = NULL;
268 gchar *readbuf = NULL;
270 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
273 chan = g_io_channel_new_file(path, "r", NULL);
276 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
277 const gchar* val = g_strdup (readbuf);
278 g_array_append_val (lines, val);
283 g_io_channel_unref (chan);
285 fprintf(stderr, "File '%s' not be read.\n", path);
292 gchar* parseenv (char* string) {
293 extern char** environ;
294 gchar* tmpstr = NULL;
298 while (environ[i] != NULL) {
299 gchar** env = g_strsplit (environ[i], "=", 2);
300 gchar* envname = g_strconcat ("$", env[0], NULL);
302 if (g_strrstr (string, envname) != NULL) {
303 tmpstr = g_strdup(string);
305 string = str_replace(envname, env[1], tmpstr);
310 g_strfreev (env); // somebody said this breaks uzbl
318 setup_signal(int signr, sigfunc *shandler) {
319 struct sigaction nh, oh;
321 nh.sa_handler = shandler;
322 sigemptyset(&nh.sa_mask);
325 if(sigaction(signr, &nh, &oh) < 0)
333 if (uzbl.behave.fifo_dir)
334 unlink (uzbl.comm.fifo_path);
335 if (uzbl.behave.socket_dir)
336 unlink (uzbl.comm.socket_path);
338 g_free(uzbl.state.executable_path);
339 g_string_free(uzbl.state.keycmd, TRUE);
340 g_hash_table_destroy(uzbl.bindings);
341 g_hash_table_destroy(uzbl.behave.commands);
344 /* used for html_mode_timeout
345 * be sure to extend this function to use
346 * more timers if needed in other places
349 set_timeout(int seconds) {
351 memset(&t, 0, sizeof t);
353 t.it_value.tv_sec = seconds;
354 t.it_value.tv_usec = 0;
355 setitimer(ITIMER_REAL, &t, NULL);
358 /* --- SIGNAL HANDLER --- */
361 catch_sigterm(int s) {
367 catch_sigint(int s) {
377 set_var_value("mode", "0");
382 /* --- CALLBACKS --- */
385 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
388 (void) navigation_action;
389 (void) policy_decision;
391 const gchar* uri = webkit_network_request_get_uri (request);
392 if (uzbl.state.verbose)
393 printf("New window requested -> %s \n", uri);
394 new_window_load_uri(uri);
399 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
404 /* If we can display it, let's display it... */
405 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
406 webkit_web_policy_decision_use (policy_decision);
410 /* ...everything we can't displayed is downloaded */
411 webkit_web_policy_decision_download (policy_decision);
416 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
420 if (uzbl.state.selected_url != NULL) {
421 if (uzbl.state.verbose)
422 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
423 new_window_load_uri(uzbl.state.selected_url);
425 if (uzbl.state.verbose)
426 printf("New web view -> %s\n","Nothing to open, exiting");
432 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
435 if (uzbl.behave.download_handler) {
436 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
437 if (uzbl.state.verbose)
438 printf("Download -> %s\n",uri);
439 /* if urls not escaped, we may have to escape and quote uri before this call */
440 run_handler(uzbl.behave.download_handler, uri);
445 /* scroll a bar in a given direction */
447 scroll (GtkAdjustment* bar, GArray *argv) {
451 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
452 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
453 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
457 scroll_begin(WebKitWebView* page, GArray *argv) {
458 (void) page; (void) argv;
459 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
463 scroll_end(WebKitWebView* page, GArray *argv) {
464 (void) page; (void) argv;
465 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
466 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
470 scroll_vert(WebKitWebView* page, GArray *argv) {
472 scroll(uzbl.gui.bar_v, argv);
476 scroll_horz(WebKitWebView* page, GArray *argv) {
478 scroll(uzbl.gui.bar_h, argv);
483 if (!uzbl.behave.show_status) {
484 gtk_widget_hide(uzbl.gui.mainbar);
486 gtk_widget_show(uzbl.gui.mainbar);
492 toggle_status_cb (WebKitWebView* page, GArray *argv) {
496 if (uzbl.behave.show_status) {
497 gtk_widget_hide(uzbl.gui.mainbar);
499 gtk_widget_show(uzbl.gui.mainbar);
501 uzbl.behave.show_status = !uzbl.behave.show_status;
506 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
510 //Set selected_url state variable
511 g_free(uzbl.state.selected_url);
512 uzbl.state.selected_url = NULL;
514 uzbl.state.selected_url = g_strdup(link);
520 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
524 if (uzbl.gui.main_title)
525 g_free (uzbl.gui.main_title);
526 uzbl.gui.main_title = g_strdup (title);
531 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
534 uzbl.gui.sbar.load_progress = progress;
539 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
543 if (uzbl.behave.load_finish_handler)
544 run_handler(uzbl.behave.load_finish_handler, "");
548 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
552 uzbl.gui.sbar.load_progress = 0;
553 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
554 if (uzbl.behave.load_start_handler)
555 run_handler(uzbl.behave.load_start_handler, "");
559 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
562 g_free (uzbl.state.uri);
563 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
564 uzbl.state.uri = g_string_free (newuri, FALSE);
565 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
566 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
569 if (uzbl.behave.load_commit_handler)
570 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
574 destroy_cb (GtkWidget* widget, gpointer data) {
582 if (uzbl.behave.history_handler) {
584 struct tm * timeinfo;
587 timeinfo = localtime ( &rawtime );
588 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
589 run_handler(uzbl.behave.history_handler, date);
594 /* VIEW funcs (little webkit wrappers) */
595 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
597 VIEWFUNC(reload_bypass_cache)
598 VIEWFUNC(stop_loading)
605 /* -- command to callback/function map for things we cannot attach to any signals */
606 static struct {char *name; Command command[2];} cmdlist[] =
607 { /* key function no_split */
608 { "back", {view_go_back, 0} },
609 { "forward", {view_go_forward, 0} },
610 { "scroll_vert", {scroll_vert, 0} },
611 { "scroll_horz", {scroll_horz, 0} },
612 { "scroll_begin", {scroll_begin, 0} },
613 { "scroll_end", {scroll_end, 0} },
614 { "reload", {view_reload, 0}, },
615 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
616 { "stop", {view_stop_loading, 0}, },
617 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
618 { "zoom_out", {view_zoom_out, 0}, },
619 { "uri", {load_uri, NOSPLIT} },
620 { "js", {run_js, NOSPLIT} },
621 { "script", {run_external_js, 0} },
622 { "toggle_status", {toggle_status_cb, 0} },
623 { "spawn", {spawn, 0} },
624 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
625 { "sh", {spawn_sh, 0} },
626 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
627 { "exit", {close_uzbl, 0} },
628 { "search", {search_forward_text, NOSPLIT} },
629 { "search_reverse", {search_reverse_text, NOSPLIT} },
630 { "dehilight", {dehilight, 0} },
631 { "toggle_insert_mode", {toggle_insert_mode, 0} },
632 { "set", {set_var, NOSPLIT} },
633 //{ "get", {get_var, NOSPLIT} },
634 { "bind", {act_bind, NOSPLIT} },
635 { "dump_config", {act_dump_config, 0} },
636 { "keycmd", {keycmd, NOSPLIT} },
637 { "keycmd_nl", {keycmd_nl, NOSPLIT} },
638 { "keycmd_bs", {keycmd_bs, 0} },
639 { "chain", {chain, 0} },
640 { "print", {print, NOSPLIT} }
647 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
649 for (i = 0; i < LENGTH(cmdlist); i++)
650 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
653 /* -- CORE FUNCTIONS -- */
656 free_action(gpointer act) {
657 Action *action = (Action*)act;
658 g_free(action->name);
660 g_free(action->param);
665 new_action(const gchar *name, const gchar *param) {
666 Action *action = g_new(Action, 1);
668 action->name = g_strdup(name);
670 action->param = g_strdup(param);
672 action->param = NULL;
678 file_exists (const char * filename) {
679 return (access(filename, F_OK) == 0);
683 set_var(WebKitWebView *page, GArray *argv) {
685 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
686 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
687 set_var_value(g_strstrip(split[0]), value);
693 print(WebKitWebView *page, GArray *argv) {
697 buf = expand_vars(argv_idx(argv, 0));
703 act_bind(WebKitWebView *page, GArray *argv) {
705 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
706 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
707 add_binding(g_strstrip(split[0]), value);
719 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
722 if (argv_idx(argv, 0)) {
723 if (strcmp (argv_idx(argv, 0), "0") == 0) {
724 uzbl.behave.insert_mode = FALSE;
726 uzbl.behave.insert_mode = TRUE;
729 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
736 load_uri (WebKitWebView *web_view, GArray *argv) {
737 if (argv_idx(argv, 0)) {
738 GString* newuri = g_string_new (argv_idx(argv, 0));
739 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
740 run_js(web_view, argv);
743 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
744 g_string_prepend (newuri, "http://");
745 /* if we do handle cookies, ask our handler for them */
746 webkit_web_view_load_uri (web_view, newuri->str);
747 g_string_free (newuri, TRUE);
752 run_js (WebKitWebView * web_view, GArray *argv) {
753 if (argv_idx(argv, 0))
754 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
758 run_external_js (WebKitWebView * web_view, GArray *argv) {
759 if (argv_idx(argv, 0)) {
760 GArray* lines = read_file_by_line (argv_idx (argv, 0));
765 while ((line = g_array_index(lines, gchar*, i))) {
767 js = g_strdup (line);
769 gchar* newjs = g_strconcat (js, line, NULL);
776 if (uzbl.state.verbose)
777 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
779 if (argv_idx (argv, 1)) {
780 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
784 webkit_web_view_execute_script (web_view, js);
786 g_array_free (lines, TRUE);
791 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
792 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
793 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
794 webkit_web_view_unmark_text_matches (page);
795 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
796 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
800 if (uzbl.state.searchtx) {
801 if (uzbl.state.verbose)
802 printf ("Searching: %s\n", uzbl.state.searchtx);
803 webkit_web_view_set_highlight_text_matches (page, TRUE);
804 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
809 search_forward_text (WebKitWebView *page, GArray *argv) {
810 search_text(page, argv, TRUE);
814 search_reverse_text (WebKitWebView *page, GArray *argv) {
815 search_text(page, argv, FALSE);
819 dehilight (WebKitWebView *page, GArray *argv) {
821 webkit_web_view_set_highlight_text_matches (page, FALSE);
826 new_window_load_uri (const gchar * uri) {
827 GString* to_execute = g_string_new ("");
828 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
830 for (i = 0; entries[i].long_name != NULL; i++) {
831 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
832 gchar** str = (gchar**)entries[i].arg_data;
834 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
838 if (uzbl.state.verbose)
839 printf("\n%s\n", to_execute->str);
840 g_spawn_command_line_async (to_execute->str, NULL);
841 g_string_free (to_execute, TRUE);
845 chain (WebKitWebView *page, GArray *argv) {
848 gchar **parts = NULL;
850 while ((a = argv_idx(argv, i++))) {
851 parts = g_strsplit (a, " ", 2);
852 parse_command(parts[0], parts[1]);
858 keycmd (WebKitWebView *page, GArray *argv) {
861 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
867 keycmd_nl (WebKitWebView *page, GArray *argv) {
870 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
876 keycmd_bs (WebKitWebView *page, GArray *argv) {
879 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
884 close_uzbl (WebKitWebView *page, GArray *argv) {
890 /* --Statusbar functions-- */
892 build_progressbar_ascii(int percent) {
893 int width=uzbl.gui.sbar.progress_w;
896 GString *bar = g_string_new("");
898 l = (double)percent*((double)width/100.);
899 l = (int)(l+.5)>=(int)l ? l+.5 : l;
901 for(i=0; i<(int)l; i++)
902 g_string_append(bar, uzbl.gui.sbar.progress_s);
905 g_string_append(bar, uzbl.gui.sbar.progress_u);
907 return g_string_free(bar, FALSE);
912 const GScannerConfig scan_config = {
915 ) /* cset_skip_characters */,
920 ) /* cset_identifier_first */,
927 ) /* cset_identifier_nth */,
928 ( "" ) /* cpair_comment_single */,
930 TRUE /* case_sensitive */,
932 FALSE /* skip_comment_multi */,
933 FALSE /* skip_comment_single */,
934 FALSE /* scan_comment_multi */,
935 TRUE /* scan_identifier */,
936 TRUE /* scan_identifier_1char */,
937 FALSE /* scan_identifier_NULL */,
938 TRUE /* scan_symbols */,
939 FALSE /* scan_binary */,
940 FALSE /* scan_octal */,
941 FALSE /* scan_float */,
942 FALSE /* scan_hex */,
943 FALSE /* scan_hex_dollar */,
944 FALSE /* scan_string_sq */,
945 FALSE /* scan_string_dq */,
946 TRUE /* numbers_2_int */,
947 FALSE /* int_2_float */,
948 FALSE /* identifier_2_string */,
949 FALSE /* char_2_token */,
950 FALSE /* symbol_2_token */,
951 TRUE /* scope_0_fallback */,
956 uzbl.scan = g_scanner_new(&scan_config);
957 while(symp->symbol_name) {
958 g_scanner_scope_add_symbol(uzbl.scan, 0,
960 GINT_TO_POINTER(symp->symbol_token));
966 expand_template(const char *template, gboolean escape_markup) {
967 if(!template) return NULL;
969 GTokenType token = G_TOKEN_NONE;
970 GString *ret = g_string_new("");
974 g_scanner_input_text(uzbl.scan, template, strlen(template));
975 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
976 token = g_scanner_get_next_token(uzbl.scan);
978 if(token == G_TOKEN_SYMBOL) {
979 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
983 buf = uzbl.state.uri?
984 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
985 g_string_append(ret, buf);
989 g_string_append(ret, uzbl.state.uri?
990 uzbl.state.uri:g_strdup(""));
993 buf = itos(uzbl.gui.sbar.load_progress);
994 g_string_append(ret, buf);
997 case SYM_LOADPRGSBAR:
998 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
999 g_string_append(ret, buf);
1004 buf = uzbl.gui.main_title?
1005 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1006 g_string_append(ret, buf);
1010 g_string_append(ret, uzbl.gui.main_title?
1011 uzbl.gui.main_title:g_strdup(""));
1013 case SYM_SELECTED_URI:
1015 buf = uzbl.state.selected_url?
1016 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1017 g_string_append(ret, buf);
1021 g_string_append(ret, uzbl.state.selected_url?
1022 uzbl.state.selected_url:g_strdup(""));
1025 buf = itos(uzbl.xwin);
1026 g_string_append(ret,
1027 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1032 buf = uzbl.state.keycmd->str?
1033 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1034 g_string_append(ret, buf);
1038 g_string_append(ret, uzbl.state.keycmd->str?
1039 uzbl.state.keycmd->str:g_strdup(""));
1042 g_string_append(ret,
1043 uzbl.behave.insert_mode?
1044 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1047 g_string_append(ret,
1048 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1050 /* useragent syms */
1052 buf = itos(WEBKIT_MAJOR_VERSION);
1053 g_string_append(ret, buf);
1057 buf = itos(WEBKIT_MINOR_VERSION);
1058 g_string_append(ret, buf);
1062 buf = itos(WEBKIT_MICRO_VERSION);
1063 g_string_append(ret, buf);
1067 g_string_append(ret, uzbl.state.unameinfo.sysname);
1070 g_string_append(ret, uzbl.state.unameinfo.nodename);
1073 g_string_append(ret, uzbl.state.unameinfo.release);
1076 g_string_append(ret, uzbl.state.unameinfo.version);
1079 g_string_append(ret, uzbl.state.unameinfo.machine);
1082 g_string_append(ret, ARCH);
1085 case SYM_DOMAINNAME:
1086 g_string_append(ret, uzbl.state.unameinfo.domainname);
1090 g_string_append(ret, COMMIT);
1096 else if(token == G_TOKEN_INT) {
1097 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1098 g_string_append(ret, buf);
1101 else if(token == G_TOKEN_IDENTIFIER) {
1102 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1104 else if(token == G_TOKEN_CHAR) {
1105 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1109 return g_string_free(ret, FALSE);
1111 /* --End Statusbar functions-- */
1114 sharg_append(GArray *a, const gchar *str) {
1115 const gchar *s = (str ? str : "");
1116 g_array_append_val(a, s);
1119 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1121 run_command (const gchar *command, const guint npre, const gchar **args,
1122 const gboolean sync, char **output_stdout) {
1123 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1126 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1127 gchar *pid = itos(getpid());
1128 gchar *xwin = itos(uzbl.xwin);
1130 sharg_append(a, command);
1131 for (i = 0; i < npre; i++) /* add n args before the default vars */
1132 sharg_append(a, args[i]);
1133 sharg_append(a, uzbl.state.config_file);
1134 sharg_append(a, pid);
1135 sharg_append(a, xwin);
1136 sharg_append(a, uzbl.comm.fifo_path);
1137 sharg_append(a, uzbl.comm.socket_path);
1138 sharg_append(a, uzbl.state.uri);
1139 sharg_append(a, uzbl.gui.main_title);
1141 for (i = npre; i < g_strv_length((gchar**)args); i++)
1142 sharg_append(a, args[i]);
1146 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1148 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1149 NULL, NULL, output_stdout, NULL, NULL, &err);
1150 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1151 NULL, NULL, NULL, &err);
1153 if (uzbl.state.verbose) {
1154 GString *s = g_string_new("spawned:");
1155 for (i = 0; i < (a->len); i++) {
1156 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1157 g_string_append_printf(s, " %s", qarg);
1160 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1161 printf("%s\n", s->str);
1162 g_string_free(s, TRUE);
1164 printf("Stdout: %s\n", *output_stdout);
1168 g_printerr("error on run_command: %s\n", err->message);
1173 g_array_free (a, TRUE);
1178 split_quoted(const gchar* src, const gboolean unquote) {
1179 /* split on unquoted space, return array of strings;
1180 remove a layer of quotes and backslashes if unquote */
1181 if (!src) return NULL;
1183 gboolean dq = FALSE;
1184 gboolean sq = FALSE;
1185 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1186 GString *s = g_string_new ("");
1190 for (p = src; *p != '\0'; p++) {
1191 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1192 else if (*p == '\\') { g_string_append_c(s, *p++);
1193 g_string_append_c(s, *p); }
1194 else if ((*p == '"') && unquote && !sq) dq = !dq;
1195 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1197 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1198 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1200 else if ((*p == ' ') && !dq && !sq) {
1201 dup = g_strdup(s->str);
1202 g_array_append_val(a, dup);
1203 g_string_truncate(s, 0);
1204 } else g_string_append_c(s, *p);
1206 dup = g_strdup(s->str);
1207 g_array_append_val(a, dup);
1208 ret = (gchar**)a->data;
1209 g_array_free (a, FALSE);
1210 g_string_free (s, TRUE);
1215 spawn(WebKitWebView *web_view, GArray *argv) {
1217 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1218 if (argv_idx(argv, 0))
1219 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1223 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1226 if (argv_idx(argv, 0))
1227 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1228 TRUE, &uzbl.comm.sync_stdout);
1232 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1234 if (!uzbl.behave.shell_cmd) {
1235 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1240 gchar *spacer = g_strdup("");
1241 g_array_insert_val(argv, 1, spacer);
1242 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1244 for (i = 1; i < g_strv_length(cmd); i++)
1245 g_array_prepend_val(argv, cmd[i]);
1247 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1253 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1255 if (!uzbl.behave.shell_cmd) {
1256 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1261 gchar *spacer = g_strdup("");
1262 g_array_insert_val(argv, 1, spacer);
1263 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1265 for (i = 1; i < g_strv_length(cmd); i++)
1266 g_array_prepend_val(argv, cmd[i]);
1268 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1269 TRUE, &uzbl.comm.sync_stdout);
1275 parse_command(const char *cmd, const char *param) {
1278 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1281 gchar **par = split_quoted(param, TRUE);
1282 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1284 if (c[1] == NOSPLIT) { /* don't split */
1285 sharg_append(a, param);
1287 for (i = 0; i < g_strv_length(par); i++)
1288 sharg_append(a, par[i]);
1290 c[0](uzbl.gui.web_view, a);
1292 g_array_free (a, TRUE);
1295 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1302 if(*uzbl.net.proxy_url == ' '
1303 || uzbl.net.proxy_url == NULL) {
1304 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1305 (GType) SOUP_SESSION_PROXY_URI);
1308 suri = soup_uri_new(uzbl.net.proxy_url);
1309 g_object_set(G_OBJECT(uzbl.net.soup_session),
1310 SOUP_SESSION_PROXY_URI,
1312 soup_uri_free(suri);
1319 if(file_exists(uzbl.gui.icon)) {
1320 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1322 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1324 g_free (uzbl.gui.icon);
1329 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1330 g_array_append_val (a, uzbl.state.uri);
1331 load_uri(uzbl.gui.web_view, a);
1332 g_array_free (a, TRUE);
1336 cmd_always_insert_mode() {
1337 uzbl.behave.insert_mode =
1338 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1344 g_object_set(G_OBJECT(uzbl.net.soup_session),
1345 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1349 cmd_max_conns_host() {
1350 g_object_set(G_OBJECT(uzbl.net.soup_session),
1351 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1356 soup_session_remove_feature
1357 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1358 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1359 /*g_free(uzbl.net.soup_logger);*/
1361 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1362 soup_session_add_feature(uzbl.net.soup_session,
1363 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1366 static WebKitWebSettings*
1368 return webkit_web_view_get_settings(uzbl.gui.web_view);
1373 WebKitWebSettings *ws = view_settings();
1374 if (uzbl.behave.font_size > 0) {
1375 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1378 if (uzbl.behave.monospace_size > 0) {
1379 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1380 uzbl.behave.monospace_size, NULL);
1382 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1383 uzbl.behave.font_size, NULL);
1389 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1393 cmd_disable_plugins() {
1394 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1395 !uzbl.behave.disable_plugins, NULL);
1399 cmd_disable_scripts() {
1400 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1401 !uzbl.behave.disable_scripts, NULL);
1405 cmd_minimum_font_size() {
1406 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1407 uzbl.behave.minimum_font_size, NULL);
1410 cmd_autoload_img() {
1411 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1412 uzbl.behave.autoload_img, NULL);
1417 cmd_autoshrink_img() {
1418 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1419 uzbl.behave.autoshrink_img, NULL);
1424 cmd_enable_spellcheck() {
1425 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1426 uzbl.behave.enable_spellcheck, NULL);
1430 cmd_enable_private() {
1431 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1432 uzbl.behave.enable_private, NULL);
1437 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1438 uzbl.behave.print_bg, NULL);
1443 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1444 uzbl.behave.style_uri, NULL);
1448 cmd_resizable_txt() {
1449 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1450 uzbl.behave.resizable_txt, NULL);
1454 cmd_default_encoding() {
1455 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1456 uzbl.behave.default_encoding, NULL);
1460 cmd_enforce_96dpi() {
1461 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1462 uzbl.behave.enforce_96dpi, NULL);
1466 cmd_caret_browsing() {
1467 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1468 uzbl.behave.caret_browsing, NULL);
1472 cmd_cookie_handler() {
1473 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1474 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1475 if ((g_strcmp0(split[0], "sh") == 0) ||
1476 (g_strcmp0(split[0], "spawn") == 0)) {
1477 g_free (uzbl.behave.cookie_handler);
1478 uzbl.behave.cookie_handler =
1479 g_strdup_printf("sync_%s %s", split[0], split[1]);
1486 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1491 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1496 if(uzbl.behave.inject_html) {
1497 webkit_web_view_load_html_string (uzbl.gui.web_view,
1498 uzbl.behave.inject_html, NULL);
1507 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1508 uzbl.behave.modmask = 0;
1510 if(uzbl.behave.modkey)
1511 g_free(uzbl.behave.modkey);
1512 uzbl.behave.modkey = buf;
1514 for (i = 0; modkeys[i].key != NULL; i++) {
1515 if (g_strrstr(buf, modkeys[i].key))
1516 uzbl.behave.modmask |= modkeys[i].mask;
1522 if (*uzbl.net.useragent == ' ') {
1523 g_free (uzbl.net.useragent);
1524 uzbl.net.useragent = NULL;
1526 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1528 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1529 g_free(uzbl.net.useragent);
1530 uzbl.net.useragent = ua;
1536 gtk_widget_ref(uzbl.gui.scrolled_win);
1537 gtk_widget_ref(uzbl.gui.mainbar);
1538 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1539 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1541 if(uzbl.behave.status_top) {
1542 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1543 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1546 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1547 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1549 gtk_widget_unref(uzbl.gui.scrolled_win);
1550 gtk_widget_unref(uzbl.gui.mainbar);
1551 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1556 set_var_value(gchar *name, gchar *val) {
1557 uzbl_cmdprop *c = NULL;
1561 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1562 /* check for the variable type */
1563 if (c->type == TYPE_STR) {
1564 buf = expand_vars(val);
1567 } else if(c->type == TYPE_INT) {
1568 int *ip = (int *)c->ptr;
1569 buf = expand_vars(val);
1570 *ip = (int)strtoul(buf, &endp, 10);
1572 } else if (c->type == TYPE_FLOAT) {
1573 float *fp = (float *)c->ptr;
1574 buf = expand_vars(val);
1575 *fp = strtod(buf, &endp);
1579 /* invoke a command specific function */
1580 if(c->func) c->func();
1587 Behaviour *b = &uzbl.behave;
1589 if(b->html_buffer->str) {
1590 webkit_web_view_load_html_string (uzbl.gui.web_view,
1591 b->html_buffer->str, b->base_url);
1592 g_string_free(b->html_buffer, TRUE);
1593 b->html_buffer = g_string_new("");
1597 enum {M_CMD, M_HTML};
1599 parse_cmd_line(const char *ctl_line) {
1600 Behaviour *b = &uzbl.behave;
1603 if(b->mode == M_HTML) {
1604 len = strlen(b->html_endmarker);
1605 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1606 if(len == strlen(ctl_line)-1 &&
1607 !strncmp(b->html_endmarker, ctl_line, len)) {
1609 set_var_value("mode", "0");
1614 set_timeout(b->html_timeout);
1615 g_string_append(b->html_buffer, ctl_line);
1618 else if((ctl_line[0] == '#') /* Comments */
1619 || (ctl_line[0] == ' ')
1620 || (ctl_line[0] == '\n'))
1621 ; /* ignore these lines */
1622 else { /* parse a command */
1624 gchar **tokens = NULL;
1625 len = strlen(ctl_line);
1627 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1628 ctlstrip = g_strndup(ctl_line, len - 1);
1629 else ctlstrip = g_strdup(ctl_line);
1631 tokens = g_strsplit(ctlstrip, " ", 2);
1632 parse_command(tokens[0], tokens[1]);
1639 build_stream_name(int type, const gchar* dir) {
1641 State *s = &uzbl.state;
1644 xwin_str = itos((int)uzbl.xwin);
1646 str = g_strdup_printf
1647 ("%s/uzbl_fifo_%s", dir,
1648 s->instance_name ? s->instance_name : xwin_str);
1649 } else if (type == SOCKET) {
1650 str = g_strdup_printf
1651 ("%s/uzbl_socket_%s", dir,
1652 s->instance_name ? s->instance_name : xwin_str );
1659 control_fifo(GIOChannel *gio, GIOCondition condition) {
1660 if (uzbl.state.verbose)
1661 printf("triggered\n");
1666 if (condition & G_IO_HUP)
1667 g_error ("Fifo: Read end of pipe died!\n");
1670 g_error ("Fifo: GIOChannel broke\n");
1672 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1673 if (ret == G_IO_STATUS_ERROR) {
1674 g_error ("Fifo: Error reading: %s\n", err->message);
1678 parse_cmd_line(ctl_line);
1685 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1686 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1687 if (unlink(uzbl.comm.fifo_path) == -1)
1688 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1689 g_free(uzbl.comm.fifo_path);
1690 uzbl.comm.fifo_path = NULL;
1693 if (*dir == ' ') { /* space unsets the variable */
1698 GIOChannel *chan = NULL;
1699 GError *error = NULL;
1700 gchar *path = build_stream_name(FIFO, dir);
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 if (uzbl.gui.main_window)
1871 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1874 if (b->status_format) {
1875 parsed = expand_template(b->status_format, TRUE);
1876 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1879 if (b->status_background) {
1881 gdk_color_parse (b->status_background, &color);
1882 //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)
1883 if (uzbl.gui.main_window)
1884 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1887 if (b->title_format_long) {
1888 parsed = expand_template(b->title_format_long, FALSE);
1889 if (uzbl.gui.main_window)
1890 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1897 key_press_cb (GtkWidget* window, GdkEventKey* event)
1899 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1903 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1904 || 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)
1907 /* turn off insert mode (if always_insert_mode is not used) */
1908 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1909 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1914 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1917 if (event->keyval == GDK_Escape) {
1918 g_string_truncate(uzbl.state.keycmd, 0);
1920 dehilight(uzbl.gui.web_view, NULL);
1924 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1925 if (event->keyval == GDK_Insert) {
1927 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1928 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1930 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1933 g_string_append (uzbl.state.keycmd, str);
1940 if (event->keyval == GDK_BackSpace)
1941 keycmd_bs(NULL, NULL);
1943 gboolean key_ret = FALSE;
1944 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1946 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1948 run_keycmd(key_ret);
1950 if (key_ret) return (!uzbl.behave.insert_mode);
1955 run_keycmd(const gboolean key_ret) {
1956 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1958 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1959 g_string_truncate(uzbl.state.keycmd, 0);
1960 parse_command(act->name, act->param);
1964 /* try if it's an incremental keycmd or one that takes args, and run it */
1965 GString* short_keys = g_string_new ("");
1966 GString* short_keys_inc = g_string_new ("");
1968 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1969 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1970 g_string_assign(short_keys_inc, short_keys->str);
1971 g_string_append_c(short_keys, '_');
1972 g_string_append_c(short_keys_inc, '*');
1974 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1975 /* run normal cmds only if return was pressed */
1976 exec_paramcmd(act, i);
1977 g_string_truncate(uzbl.state.keycmd, 0);
1979 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1980 if (key_ret) /* just quit the incremental command on return */
1981 g_string_truncate(uzbl.state.keycmd, 0);
1982 else exec_paramcmd(act, i); /* otherwise execute the incremental */
1986 g_string_truncate(short_keys, short_keys->len - 1);
1988 g_string_free (short_keys, TRUE);
1989 g_string_free (short_keys_inc, TRUE);
1993 exec_paramcmd(const Action *act, const guint i) {
1994 GString *parampart = g_string_new (uzbl.state.keycmd->str);
1995 GString *actionname = g_string_new ("");
1996 GString *actionparam = g_string_new ("");
1997 g_string_erase (parampart, 0, i+1);
1999 g_string_printf (actionname, act->name, parampart->str);
2001 g_string_printf (actionparam, act->param, parampart->str);
2002 parse_command(actionname->str, actionparam->str);
2003 g_string_free(actionname, TRUE);
2004 g_string_free(actionparam, TRUE);
2005 g_string_free(parampart, TRUE);
2013 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2014 //main_window_ref = g_object_ref(scrolled_window);
2015 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
2017 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2018 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2020 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2021 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2022 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2023 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2024 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2025 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2026 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2027 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2028 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2029 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2030 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2032 return scrolled_window;
2039 g->mainbar = gtk_hbox_new (FALSE, 0);
2041 /* keep a reference to the bar so we can re-pack it at runtime*/
2042 //sbar_ref = g_object_ref(g->mainbar);
2044 g->mainbar_label = gtk_label_new ("");
2045 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2046 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2047 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2048 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2049 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2050 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2055 GtkWidget* create_window () {
2056 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2057 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2058 gtk_widget_set_name (window, "Uzbl browser");
2059 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2060 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2066 GtkPlug* create_plug () {
2067 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2068 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2069 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2076 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2078 If actname is one that calls an external command, this function will inject
2079 newargs in front of the user-provided args in that command line. They will
2080 come become after the body of the script (in sh) or after the name of
2081 the command to execute (in spawn).
2082 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2083 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2085 The return value consist of two strings: the action (sh, ...) and its args.
2087 If act is not one that calls an external command, then the given action merely
2090 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2091 gchar *actdup = g_strdup(actname);
2092 g_array_append_val(rets, actdup);
2094 if ((g_strcmp0(actname, "spawn") == 0) ||
2095 (g_strcmp0(actname, "sh") == 0) ||
2096 (g_strcmp0(actname, "sync_spawn") == 0) ||
2097 (g_strcmp0(actname, "sync_sh") == 0)) {
2099 GString *a = g_string_new("");
2100 gchar **spawnparts = split_quoted(origargs, FALSE);
2101 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2102 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2104 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2105 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2107 g_array_append_val(rets, a->str);
2108 g_string_free(a, FALSE);
2109 g_strfreev(spawnparts);
2111 gchar *origdup = g_strdup(origargs);
2112 g_array_append_val(rets, origdup);
2114 return (gchar**)g_array_free(rets, FALSE);
2118 run_handler (const gchar *act, const gchar *args) {
2119 /* Consider this code a temporary hack to make the handlers usable.
2120 In practice, all this splicing, injection, and reconstruction is
2121 inefficient, annoying and hard to manage. Potential pitfalls arise
2122 when the handler specific args 1) are not quoted (the handler
2123 callbacks should take care of this) 2) are quoted but interfere
2124 with the users' own quotation. A more ideal solution is
2125 to refactor parse_command so that it doesn't just take a string
2126 and execute it; rather than that, we should have a function which
2127 returns the argument vector parsed from the string. This vector
2128 could be modified (e.g. insert additional args into it) before
2129 passing it to the next function that actually executes it. Though
2130 it still isn't perfect for chain actions.. will reconsider & re-
2131 factor when I have the time. -duc */
2133 char **parts = g_strsplit(act, " ", 2);
2135 if (g_strcmp0(parts[0], "chain") == 0) {
2136 GString *newargs = g_string_new("");
2137 gchar **chainparts = split_quoted(parts[1], FALSE);
2139 /* for every argument in the chain, inject the handler args
2140 and make sure the new parts are wrapped in quotes */
2141 gchar **cp = chainparts;
2143 gchar *quotless = NULL;
2144 gchar **spliced_quotless = NULL; // sigh -_-;
2145 gchar **inpart = NULL;
2148 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2150 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2151 } else quotless = g_strdup(*cp);
2153 spliced_quotless = g_strsplit(quotless, " ", 2);
2154 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2155 g_strfreev(spliced_quotless);
2157 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2163 parse_command(parts[0], &(newargs->str[1]));
2164 g_string_free(newargs, TRUE);
2165 g_strfreev(chainparts);
2168 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2169 parse_command(inparts[0], inparts[1]);
2177 add_binding (const gchar *key, const gchar *act) {
2178 char **parts = g_strsplit(act, " ", 2);
2185 if (uzbl.state.verbose)
2186 printf ("Binding %-10s : %s\n", key, act);
2187 action = new_action(parts[0], parts[1]);
2189 if (g_hash_table_remove (uzbl.bindings, key))
2190 g_warning ("Overwriting existing binding for \"%s\"", key);
2191 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2196 get_xdg_var (XDG_Var xdg) {
2197 const gchar* actual_value = getenv (xdg.environmental);
2198 const gchar* home = getenv ("HOME");
2199 gchar* return_value;
2201 if (! actual_value || strcmp (actual_value, "") == 0) {
2202 if (xdg.default_value) {
2203 return_value = str_replace ("~", home, xdg.default_value);
2205 return_value = NULL;
2208 return_value = str_replace("~", home, actual_value);
2211 return return_value;
2215 find_xdg_file (int xdg_type, char* filename) {
2216 /* xdg_type = 0 => config
2217 xdg_type = 1 => data
2218 xdg_type = 2 => cache*/
2220 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2221 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2224 gchar* temporary_string;
2228 if (! file_exists (temporary_file) && xdg_type != 2) {
2229 buf = get_xdg_var (XDG[3 + xdg_type]);
2230 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2233 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2234 g_free (temporary_file);
2235 temporary_file = g_strconcat (temporary_string, filename, NULL);
2239 //g_free (temporary_string); - segfaults.
2241 if (file_exists (temporary_file)) {
2242 return temporary_file;
2249 State *s = &uzbl.state;
2250 Network *n = &uzbl.net;
2252 for (i = 0; default_config[i].command != NULL; i++) {
2253 parse_cmd_line(default_config[i].command);
2256 if (!s->config_file) {
2257 s->config_file = find_xdg_file (0, "/uzbl/config");
2260 if (s->config_file) {
2261 GArray* lines = read_file_by_line (s->config_file);
2265 while ((line = g_array_index(lines, gchar*, i))) {
2266 parse_cmd_line (line);
2270 g_array_free (lines, TRUE);
2272 if (uzbl.state.verbose)
2273 printf ("No configuration file loaded.\n");
2276 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2279 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2282 if (!uzbl.behave.cookie_handler)
2285 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2286 GString *s = g_string_new ("");
2287 SoupURI * soup_uri = soup_message_get_uri(msg);
2288 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2289 run_handler(uzbl.behave.cookie_handler, s->str);
2291 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2292 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2293 if ( p != NULL ) *p = '\0';
2294 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2296 if (uzbl.comm.sync_stdout)
2297 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2299 g_string_free(s, TRUE);
2303 save_cookies (SoupMessage *msg, gpointer user_data){
2307 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2308 cookie = soup_cookie_to_set_cookie_header(ck->data);
2309 SoupURI * soup_uri = soup_message_get_uri(msg);
2310 GString *s = g_string_new ("");
2311 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2312 run_handler(uzbl.behave.cookie_handler, s->str);
2314 g_string_free(s, TRUE);
2319 /* --- WEBINSPECTOR --- */
2321 hide_window_cb(GtkWidget *widget, gpointer data) {
2324 gtk_widget_hide(widget);
2327 static WebKitWebView*
2328 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2331 (void) web_inspector;
2332 GtkWidget* scrolled_window;
2333 GtkWidget* new_web_view;
2336 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2337 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2338 G_CALLBACK(hide_window_cb), NULL);
2340 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2341 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2342 gtk_widget_show(g->inspector_window);
2344 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2345 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2346 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2347 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2348 gtk_widget_show(scrolled_window);
2350 new_web_view = webkit_web_view_new();
2351 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2353 return WEBKIT_WEB_VIEW(new_web_view);
2357 inspector_show_window_cb (WebKitWebInspector* inspector){
2359 gtk_widget_show(uzbl.gui.inspector_window);
2363 /* TODO: Add variables and code to make use of these functions */
2365 inspector_close_window_cb (WebKitWebInspector* inspector){
2371 inspector_attach_window_cb (WebKitWebInspector* inspector){
2377 inspector_detach_window_cb (WebKitWebInspector* inspector){
2383 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2389 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2395 set_up_inspector() {
2397 WebKitWebSettings *settings = view_settings();
2398 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2400 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2401 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2402 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2403 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2404 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2405 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2406 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2408 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2412 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2414 uzbl_cmdprop *c = v;
2419 if(c->type == TYPE_STR)
2420 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2421 else if(c->type == TYPE_INT)
2422 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2426 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2430 printf("bind %s = %s %s\n", (char *)k ,
2431 (char *)a->name, a->param?(char *)a->param:"");
2436 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2437 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2442 main (int argc, char* argv[]) {
2443 gtk_init (&argc, &argv);
2444 if (!g_thread_supported ())
2445 g_thread_init (NULL);
2446 uzbl.state.executable_path = g_strdup(argv[0]);
2447 uzbl.state.selected_url = NULL;
2448 uzbl.state.searchtx = NULL;
2450 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2451 g_option_context_add_main_entries (context, entries, NULL);
2452 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2453 g_option_context_parse (context, &argc, &argv, NULL);
2454 g_option_context_free(context);
2456 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2457 gboolean verbose_override = uzbl.state.verbose;
2459 /* initialize hash table */
2460 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2462 uzbl.net.soup_session = webkit_get_default_session();
2463 uzbl.state.keycmd = g_string_new("");
2465 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2466 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2467 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2468 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2469 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2470 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2473 if(uname(&uzbl.state.unameinfo) == -1)
2474 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2476 uzbl.gui.sbar.progress_s = g_strdup("=");
2477 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2478 uzbl.gui.sbar.progress_w = 10;
2480 /* HTML mode defaults*/
2481 uzbl.behave.html_buffer = g_string_new("");
2482 uzbl.behave.html_endmarker = g_strdup(".");
2483 uzbl.behave.html_timeout = 60;
2484 uzbl.behave.base_url = g_strdup("http://invalid");
2486 /* default mode indicators */
2487 uzbl.behave.insert_indicator = g_strdup("I");
2488 uzbl.behave.cmd_indicator = g_strdup("C");
2492 make_var_to_name_hash();
2494 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2496 uzbl.gui.scrolled_win = create_browser();
2499 /* initial packing */
2500 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2501 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2503 if (uzbl.state.socket_id) {
2504 uzbl.gui.plug = create_plug ();
2505 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2506 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2508 uzbl.gui.main_window = create_window ();
2509 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2510 gtk_widget_show_all (uzbl.gui.main_window);
2511 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2514 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2516 if (uzbl.state.verbose) {
2517 printf("Uzbl start location: %s\n", argv[0]);
2518 if (uzbl.state.socket_id)
2519 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2521 printf("window_id %i\n",(int) uzbl.xwin);
2522 printf("pid %i\n", getpid ());
2523 printf("name: %s\n", uzbl.state.instance_name);
2526 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2527 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2528 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2529 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2530 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2534 if (!uzbl.behave.show_status)
2535 gtk_widget_hide(uzbl.gui.mainbar);
2544 if (verbose_override > uzbl.state.verbose)
2545 uzbl.state.verbose = verbose_override;
2548 set_var_value("uri", uri_override);
2549 g_free(uri_override);
2550 } else if (uzbl.state.uri)
2551 cmd_load_uri(uzbl.gui.web_view, NULL);
2556 return EXIT_SUCCESS;
2559 /* vi: set et ts=4: */