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_zoom_type (WebKitWebView* page, GArray *argv) {
496 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
500 toggle_status_cb (WebKitWebView* page, GArray *argv) {
504 if (uzbl.behave.show_status) {
505 gtk_widget_hide(uzbl.gui.mainbar);
507 gtk_widget_show(uzbl.gui.mainbar);
509 uzbl.behave.show_status = !uzbl.behave.show_status;
514 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
518 //Set selected_url state variable
519 g_free(uzbl.state.selected_url);
520 uzbl.state.selected_url = NULL;
522 uzbl.state.selected_url = g_strdup(link);
528 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
532 if (uzbl.gui.main_title)
533 g_free (uzbl.gui.main_title);
534 uzbl.gui.main_title = g_strdup (title);
539 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
542 uzbl.gui.sbar.load_progress = progress;
547 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
551 if (uzbl.behave.load_finish_handler)
552 run_handler(uzbl.behave.load_finish_handler, "");
556 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
560 uzbl.gui.sbar.load_progress = 0;
561 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
562 if (uzbl.behave.load_start_handler)
563 run_handler(uzbl.behave.load_start_handler, "");
567 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
570 g_free (uzbl.state.uri);
571 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
572 uzbl.state.uri = g_string_free (newuri, FALSE);
573 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
574 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
577 if (uzbl.behave.load_commit_handler)
578 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
582 destroy_cb (GtkWidget* widget, gpointer data) {
590 if (uzbl.behave.history_handler) {
592 struct tm * timeinfo;
595 timeinfo = localtime ( &rawtime );
596 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
597 run_handler(uzbl.behave.history_handler, date);
602 /* VIEW funcs (little webkit wrappers) */
603 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
605 VIEWFUNC(reload_bypass_cache)
606 VIEWFUNC(stop_loading)
613 /* -- command to callback/function map for things we cannot attach to any signals */
614 static struct {char *name; Command command[2];} cmdlist[] =
615 { /* key function no_split */
616 { "back", {view_go_back, 0} },
617 { "forward", {view_go_forward, 0} },
618 { "scroll_vert", {scroll_vert, 0} },
619 { "scroll_horz", {scroll_horz, 0} },
620 { "scroll_begin", {scroll_begin, 0} },
621 { "scroll_end", {scroll_end, 0} },
622 { "reload", {view_reload, 0}, },
623 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
624 { "stop", {view_stop_loading, 0}, },
625 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
626 { "zoom_out", {view_zoom_out, 0}, },
627 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
628 { "uri", {load_uri, NOSPLIT} },
629 { "js", {run_js, NOSPLIT} },
630 { "script", {run_external_js, 0} },
631 { "toggle_status", {toggle_status_cb, 0} },
632 { "spawn", {spawn, 0} },
633 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
634 { "sh", {spawn_sh, 0} },
635 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
636 { "exit", {close_uzbl, 0} },
637 { "search", {search_forward_text, NOSPLIT} },
638 { "search_reverse", {search_reverse_text, NOSPLIT} },
639 { "dehilight", {dehilight, 0} },
640 { "toggle_insert_mode", {toggle_insert_mode, 0} },
641 { "set", {set_var, NOSPLIT} },
642 //{ "get", {get_var, NOSPLIT} },
643 { "bind", {act_bind, NOSPLIT} },
644 { "dump_config", {act_dump_config, 0} },
645 { "keycmd", {keycmd, NOSPLIT} },
646 { "keycmd_nl", {keycmd_nl, NOSPLIT} },
647 { "keycmd_bs", {keycmd_bs, 0} },
648 { "chain", {chain, 0} },
649 { "print", {print, NOSPLIT} }
656 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
658 for (i = 0; i < LENGTH(cmdlist); i++)
659 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
662 /* -- CORE FUNCTIONS -- */
665 free_action(gpointer act) {
666 Action *action = (Action*)act;
667 g_free(action->name);
669 g_free(action->param);
674 new_action(const gchar *name, const gchar *param) {
675 Action *action = g_new(Action, 1);
677 action->name = g_strdup(name);
679 action->param = g_strdup(param);
681 action->param = NULL;
687 file_exists (const char * filename) {
688 return (access(filename, F_OK) == 0);
692 set_var(WebKitWebView *page, GArray *argv) {
694 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
695 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
696 set_var_value(g_strstrip(split[0]), value);
702 print(WebKitWebView *page, GArray *argv) {
706 buf = expand_vars(argv_idx(argv, 0));
712 act_bind(WebKitWebView *page, GArray *argv) {
714 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
715 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
716 add_binding(g_strstrip(split[0]), value);
728 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
731 if (argv_idx(argv, 0)) {
732 if (strcmp (argv_idx(argv, 0), "0") == 0) {
733 uzbl.behave.insert_mode = FALSE;
735 uzbl.behave.insert_mode = TRUE;
738 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
745 load_uri (WebKitWebView *web_view, GArray *argv) {
746 if (argv_idx(argv, 0)) {
747 GString* newuri = g_string_new (argv_idx(argv, 0));
748 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
749 run_js(web_view, argv);
752 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
753 g_string_prepend (newuri, "http://");
754 /* if we do handle cookies, ask our handler for them */
755 webkit_web_view_load_uri (web_view, newuri->str);
756 g_string_free (newuri, TRUE);
761 run_js (WebKitWebView * web_view, GArray *argv) {
762 if (argv_idx(argv, 0))
763 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
767 run_external_js (WebKitWebView * web_view, GArray *argv) {
768 if (argv_idx(argv, 0)) {
769 GArray* lines = read_file_by_line (argv_idx (argv, 0));
774 while ((line = g_array_index(lines, gchar*, i))) {
776 js = g_strdup (line);
778 gchar* newjs = g_strconcat (js, line, NULL);
785 if (uzbl.state.verbose)
786 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
788 if (argv_idx (argv, 1)) {
789 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
793 webkit_web_view_execute_script (web_view, js);
795 g_array_free (lines, TRUE);
800 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
801 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
802 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
803 webkit_web_view_unmark_text_matches (page);
804 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
805 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
809 if (uzbl.state.searchtx) {
810 if (uzbl.state.verbose)
811 printf ("Searching: %s\n", uzbl.state.searchtx);
812 webkit_web_view_set_highlight_text_matches (page, TRUE);
813 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
818 search_forward_text (WebKitWebView *page, GArray *argv) {
819 search_text(page, argv, TRUE);
823 search_reverse_text (WebKitWebView *page, GArray *argv) {
824 search_text(page, argv, FALSE);
828 dehilight (WebKitWebView *page, GArray *argv) {
830 webkit_web_view_set_highlight_text_matches (page, FALSE);
835 new_window_load_uri (const gchar * uri) {
836 GString* to_execute = g_string_new ("");
837 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
839 for (i = 0; entries[i].long_name != NULL; i++) {
840 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
841 gchar** str = (gchar**)entries[i].arg_data;
843 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
847 if (uzbl.state.verbose)
848 printf("\n%s\n", to_execute->str);
849 g_spawn_command_line_async (to_execute->str, NULL);
850 g_string_free (to_execute, TRUE);
854 chain (WebKitWebView *page, GArray *argv) {
857 gchar **parts = NULL;
859 while ((a = argv_idx(argv, i++))) {
860 parts = g_strsplit (a, " ", 2);
861 parse_command(parts[0], parts[1]);
867 keycmd (WebKitWebView *page, GArray *argv) {
870 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
876 keycmd_nl (WebKitWebView *page, GArray *argv) {
879 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
885 keycmd_bs (WebKitWebView *page, GArray *argv) {
888 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
893 close_uzbl (WebKitWebView *page, GArray *argv) {
899 /* --Statusbar functions-- */
901 build_progressbar_ascii(int percent) {
902 int width=uzbl.gui.sbar.progress_w;
905 GString *bar = g_string_new("");
907 l = (double)percent*((double)width/100.);
908 l = (int)(l+.5)>=(int)l ? l+.5 : l;
910 for(i=0; i<(int)l; i++)
911 g_string_append(bar, uzbl.gui.sbar.progress_s);
914 g_string_append(bar, uzbl.gui.sbar.progress_u);
916 return g_string_free(bar, FALSE);
921 const GScannerConfig scan_config = {
924 ) /* cset_skip_characters */,
929 ) /* cset_identifier_first */,
936 ) /* cset_identifier_nth */,
937 ( "" ) /* cpair_comment_single */,
939 TRUE /* case_sensitive */,
941 FALSE /* skip_comment_multi */,
942 FALSE /* skip_comment_single */,
943 FALSE /* scan_comment_multi */,
944 TRUE /* scan_identifier */,
945 TRUE /* scan_identifier_1char */,
946 FALSE /* scan_identifier_NULL */,
947 TRUE /* scan_symbols */,
948 FALSE /* scan_binary */,
949 FALSE /* scan_octal */,
950 FALSE /* scan_float */,
951 FALSE /* scan_hex */,
952 FALSE /* scan_hex_dollar */,
953 FALSE /* scan_string_sq */,
954 FALSE /* scan_string_dq */,
955 TRUE /* numbers_2_int */,
956 FALSE /* int_2_float */,
957 FALSE /* identifier_2_string */,
958 FALSE /* char_2_token */,
959 FALSE /* symbol_2_token */,
960 TRUE /* scope_0_fallback */,
965 uzbl.scan = g_scanner_new(&scan_config);
966 while(symp->symbol_name) {
967 g_scanner_scope_add_symbol(uzbl.scan, 0,
969 GINT_TO_POINTER(symp->symbol_token));
975 expand_template(const char *template, gboolean escape_markup) {
976 if(!template) return NULL;
978 GTokenType token = G_TOKEN_NONE;
979 GString *ret = g_string_new("");
983 g_scanner_input_text(uzbl.scan, template, strlen(template));
984 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
985 token = g_scanner_get_next_token(uzbl.scan);
987 if(token == G_TOKEN_SYMBOL) {
988 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
992 buf = uzbl.state.uri?
993 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
994 g_string_append(ret, buf);
998 g_string_append(ret, uzbl.state.uri?
999 uzbl.state.uri:g_strdup(""));
1002 buf = itos(uzbl.gui.sbar.load_progress);
1003 g_string_append(ret, buf);
1006 case SYM_LOADPRGSBAR:
1007 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1008 g_string_append(ret, buf);
1013 buf = uzbl.gui.main_title?
1014 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1015 g_string_append(ret, buf);
1019 g_string_append(ret, uzbl.gui.main_title?
1020 uzbl.gui.main_title:g_strdup(""));
1022 case SYM_SELECTED_URI:
1024 buf = uzbl.state.selected_url?
1025 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1026 g_string_append(ret, buf);
1030 g_string_append(ret, uzbl.state.selected_url?
1031 uzbl.state.selected_url:g_strdup(""));
1034 buf = itos(uzbl.xwin);
1035 g_string_append(ret,
1036 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1041 buf = uzbl.state.keycmd->str?
1042 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1043 g_string_append(ret, buf);
1047 g_string_append(ret, uzbl.state.keycmd->str?
1048 uzbl.state.keycmd->str:g_strdup(""));
1051 g_string_append(ret,
1052 uzbl.behave.insert_mode?
1053 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1056 g_string_append(ret,
1057 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1059 /* useragent syms */
1061 buf = itos(WEBKIT_MAJOR_VERSION);
1062 g_string_append(ret, buf);
1066 buf = itos(WEBKIT_MINOR_VERSION);
1067 g_string_append(ret, buf);
1071 buf = itos(WEBKIT_MICRO_VERSION);
1072 g_string_append(ret, buf);
1076 g_string_append(ret, uzbl.state.unameinfo.sysname);
1079 g_string_append(ret, uzbl.state.unameinfo.nodename);
1082 g_string_append(ret, uzbl.state.unameinfo.release);
1085 g_string_append(ret, uzbl.state.unameinfo.version);
1088 g_string_append(ret, uzbl.state.unameinfo.machine);
1091 g_string_append(ret, ARCH);
1094 case SYM_DOMAINNAME:
1095 g_string_append(ret, uzbl.state.unameinfo.domainname);
1099 g_string_append(ret, COMMIT);
1105 else if(token == G_TOKEN_INT) {
1106 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1107 g_string_append(ret, buf);
1110 else if(token == G_TOKEN_IDENTIFIER) {
1111 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1113 else if(token == G_TOKEN_CHAR) {
1114 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1118 return g_string_free(ret, FALSE);
1120 /* --End Statusbar functions-- */
1123 sharg_append(GArray *a, const gchar *str) {
1124 const gchar *s = (str ? str : "");
1125 g_array_append_val(a, s);
1128 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1130 run_command (const gchar *command, const guint npre, const gchar **args,
1131 const gboolean sync, char **output_stdout) {
1132 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1135 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1136 gchar *pid = itos(getpid());
1137 gchar *xwin = itos(uzbl.xwin);
1139 sharg_append(a, command);
1140 for (i = 0; i < npre; i++) /* add n args before the default vars */
1141 sharg_append(a, args[i]);
1142 sharg_append(a, uzbl.state.config_file);
1143 sharg_append(a, pid);
1144 sharg_append(a, xwin);
1145 sharg_append(a, uzbl.comm.fifo_path);
1146 sharg_append(a, uzbl.comm.socket_path);
1147 sharg_append(a, uzbl.state.uri);
1148 sharg_append(a, uzbl.gui.main_title);
1150 for (i = npre; i < g_strv_length((gchar**)args); i++)
1151 sharg_append(a, args[i]);
1155 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1157 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1158 NULL, NULL, output_stdout, NULL, NULL, &err);
1159 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1160 NULL, NULL, NULL, &err);
1162 if (uzbl.state.verbose) {
1163 GString *s = g_string_new("spawned:");
1164 for (i = 0; i < (a->len); i++) {
1165 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1166 g_string_append_printf(s, " %s", qarg);
1169 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1170 printf("%s\n", s->str);
1171 g_string_free(s, TRUE);
1173 printf("Stdout: %s\n", *output_stdout);
1177 g_printerr("error on run_command: %s\n", err->message);
1182 g_array_free (a, TRUE);
1187 split_quoted(const gchar* src, const gboolean unquote) {
1188 /* split on unquoted space, return array of strings;
1189 remove a layer of quotes and backslashes if unquote */
1190 if (!src) return NULL;
1192 gboolean dq = FALSE;
1193 gboolean sq = FALSE;
1194 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1195 GString *s = g_string_new ("");
1199 for (p = src; *p != '\0'; p++) {
1200 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1201 else if (*p == '\\') { g_string_append_c(s, *p++);
1202 g_string_append_c(s, *p); }
1203 else if ((*p == '"') && unquote && !sq) dq = !dq;
1204 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1206 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1207 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1209 else if ((*p == ' ') && !dq && !sq) {
1210 dup = g_strdup(s->str);
1211 g_array_append_val(a, dup);
1212 g_string_truncate(s, 0);
1213 } else g_string_append_c(s, *p);
1215 dup = g_strdup(s->str);
1216 g_array_append_val(a, dup);
1217 ret = (gchar**)a->data;
1218 g_array_free (a, FALSE);
1219 g_string_free (s, TRUE);
1224 spawn(WebKitWebView *web_view, GArray *argv) {
1226 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1227 if (argv_idx(argv, 0))
1228 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1232 spawn_sync(WebKitWebView *web_view, GArray *argv) {
1235 if (argv_idx(argv, 0))
1236 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1237 TRUE, &uzbl.comm.sync_stdout);
1241 spawn_sh(WebKitWebView *web_view, GArray *argv) {
1243 if (!uzbl.behave.shell_cmd) {
1244 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1249 gchar *spacer = g_strdup("");
1250 g_array_insert_val(argv, 1, spacer);
1251 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1253 for (i = 1; i < g_strv_length(cmd); i++)
1254 g_array_prepend_val(argv, cmd[i]);
1256 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1262 spawn_sh_sync(WebKitWebView *web_view, GArray *argv) {
1264 if (!uzbl.behave.shell_cmd) {
1265 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1270 gchar *spacer = g_strdup("");
1271 g_array_insert_val(argv, 1, spacer);
1272 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1274 for (i = 1; i < g_strv_length(cmd); i++)
1275 g_array_prepend_val(argv, cmd[i]);
1277 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1278 TRUE, &uzbl.comm.sync_stdout);
1284 parse_command(const char *cmd, const char *param) {
1287 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1290 gchar **par = split_quoted(param, TRUE);
1291 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1293 if (c[1] == NOSPLIT) { /* don't split */
1294 sharg_append(a, param);
1296 for (i = 0; i < g_strv_length(par); i++)
1297 sharg_append(a, par[i]);
1299 c[0](uzbl.gui.web_view, a);
1301 g_array_free (a, TRUE);
1304 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1311 if(*uzbl.net.proxy_url == ' '
1312 || uzbl.net.proxy_url == NULL) {
1313 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1314 (GType) SOUP_SESSION_PROXY_URI);
1317 suri = soup_uri_new(uzbl.net.proxy_url);
1318 g_object_set(G_OBJECT(uzbl.net.soup_session),
1319 SOUP_SESSION_PROXY_URI,
1321 soup_uri_free(suri);
1328 if(file_exists(uzbl.gui.icon)) {
1329 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1331 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1333 g_free (uzbl.gui.icon);
1338 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1339 g_array_append_val (a, uzbl.state.uri);
1340 load_uri(uzbl.gui.web_view, a);
1341 g_array_free (a, TRUE);
1345 cmd_always_insert_mode() {
1346 uzbl.behave.insert_mode =
1347 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1353 g_object_set(G_OBJECT(uzbl.net.soup_session),
1354 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1358 cmd_max_conns_host() {
1359 g_object_set(G_OBJECT(uzbl.net.soup_session),
1360 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1365 soup_session_remove_feature
1366 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1367 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1368 /*g_free(uzbl.net.soup_logger);*/
1370 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1371 soup_session_add_feature(uzbl.net.soup_session,
1372 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1375 static WebKitWebSettings*
1377 return webkit_web_view_get_settings(uzbl.gui.web_view);
1382 WebKitWebSettings *ws = view_settings();
1383 if (uzbl.behave.font_size > 0) {
1384 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1387 if (uzbl.behave.monospace_size > 0) {
1388 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1389 uzbl.behave.monospace_size, NULL);
1391 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1392 uzbl.behave.font_size, NULL);
1398 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1402 cmd_disable_plugins() {
1403 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1404 !uzbl.behave.disable_plugins, NULL);
1408 cmd_disable_scripts() {
1409 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1410 !uzbl.behave.disable_scripts, NULL);
1414 cmd_minimum_font_size() {
1415 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1416 uzbl.behave.minimum_font_size, NULL);
1419 cmd_autoload_img() {
1420 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1421 uzbl.behave.autoload_img, NULL);
1426 cmd_autoshrink_img() {
1427 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1428 uzbl.behave.autoshrink_img, NULL);
1433 cmd_enable_spellcheck() {
1434 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1435 uzbl.behave.enable_spellcheck, NULL);
1439 cmd_enable_private() {
1440 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1441 uzbl.behave.enable_private, NULL);
1446 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1447 uzbl.behave.print_bg, NULL);
1452 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1453 uzbl.behave.style_uri, NULL);
1457 cmd_resizable_txt() {
1458 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1459 uzbl.behave.resizable_txt, NULL);
1463 cmd_default_encoding() {
1464 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1465 uzbl.behave.default_encoding, NULL);
1469 cmd_enforce_96dpi() {
1470 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1471 uzbl.behave.enforce_96dpi, NULL);
1475 cmd_caret_browsing() {
1476 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1477 uzbl.behave.caret_browsing, NULL);
1481 cmd_cookie_handler() {
1482 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1483 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1484 if ((g_strcmp0(split[0], "sh") == 0) ||
1485 (g_strcmp0(split[0], "spawn") == 0)) {
1486 g_free (uzbl.behave.cookie_handler);
1487 uzbl.behave.cookie_handler =
1488 g_strdup_printf("sync_%s %s", split[0], split[1]);
1495 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1500 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1505 if(uzbl.behave.inject_html) {
1506 webkit_web_view_load_html_string (uzbl.gui.web_view,
1507 uzbl.behave.inject_html, NULL);
1516 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1517 uzbl.behave.modmask = 0;
1519 if(uzbl.behave.modkey)
1520 g_free(uzbl.behave.modkey);
1521 uzbl.behave.modkey = buf;
1523 for (i = 0; modkeys[i].key != NULL; i++) {
1524 if (g_strrstr(buf, modkeys[i].key))
1525 uzbl.behave.modmask |= modkeys[i].mask;
1531 if (*uzbl.net.useragent == ' ') {
1532 g_free (uzbl.net.useragent);
1533 uzbl.net.useragent = NULL;
1535 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1537 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1538 g_free(uzbl.net.useragent);
1539 uzbl.net.useragent = ua;
1545 gtk_widget_ref(uzbl.gui.scrolled_win);
1546 gtk_widget_ref(uzbl.gui.mainbar);
1547 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1548 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1550 if(uzbl.behave.status_top) {
1551 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1552 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1555 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1556 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1558 gtk_widget_unref(uzbl.gui.scrolled_win);
1559 gtk_widget_unref(uzbl.gui.mainbar);
1560 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1565 set_var_value(gchar *name, gchar *val) {
1566 uzbl_cmdprop *c = NULL;
1570 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1571 /* check for the variable type */
1572 if (c->type == TYPE_STR) {
1573 buf = expand_vars(val);
1576 } else if(c->type == TYPE_INT) {
1577 int *ip = (int *)c->ptr;
1578 buf = expand_vars(val);
1579 *ip = (int)strtoul(buf, &endp, 10);
1581 } else if (c->type == TYPE_FLOAT) {
1582 float *fp = (float *)c->ptr;
1583 buf = expand_vars(val);
1584 *fp = strtod(buf, &endp);
1588 /* invoke a command specific function */
1589 if(c->func) c->func();
1596 Behaviour *b = &uzbl.behave;
1598 if(b->html_buffer->str) {
1599 webkit_web_view_load_html_string (uzbl.gui.web_view,
1600 b->html_buffer->str, b->base_url);
1601 g_string_free(b->html_buffer, TRUE);
1602 b->html_buffer = g_string_new("");
1606 enum {M_CMD, M_HTML};
1608 parse_cmd_line(const char *ctl_line) {
1609 Behaviour *b = &uzbl.behave;
1612 if(b->mode == M_HTML) {
1613 len = strlen(b->html_endmarker);
1614 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1615 if(len == strlen(ctl_line)-1 &&
1616 !strncmp(b->html_endmarker, ctl_line, len)) {
1618 set_var_value("mode", "0");
1623 set_timeout(b->html_timeout);
1624 g_string_append(b->html_buffer, ctl_line);
1627 else if((ctl_line[0] == '#') /* Comments */
1628 || (ctl_line[0] == ' ')
1629 || (ctl_line[0] == '\n'))
1630 ; /* ignore these lines */
1631 else { /* parse a command */
1633 gchar **tokens = NULL;
1634 len = strlen(ctl_line);
1636 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1637 ctlstrip = g_strndup(ctl_line, len - 1);
1638 else ctlstrip = g_strdup(ctl_line);
1640 tokens = g_strsplit(ctlstrip, " ", 2);
1641 parse_command(tokens[0], tokens[1]);
1648 build_stream_name(int type, const gchar* dir) {
1650 State *s = &uzbl.state;
1653 xwin_str = itos((int)uzbl.xwin);
1655 str = g_strdup_printf
1656 ("%s/uzbl_fifo_%s", dir,
1657 s->instance_name ? s->instance_name : xwin_str);
1658 } else if (type == SOCKET) {
1659 str = g_strdup_printf
1660 ("%s/uzbl_socket_%s", dir,
1661 s->instance_name ? s->instance_name : xwin_str );
1668 control_fifo(GIOChannel *gio, GIOCondition condition) {
1669 if (uzbl.state.verbose)
1670 printf("triggered\n");
1675 if (condition & G_IO_HUP)
1676 g_error ("Fifo: Read end of pipe died!\n");
1679 g_error ("Fifo: GIOChannel broke\n");
1681 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1682 if (ret == G_IO_STATUS_ERROR) {
1683 g_error ("Fifo: Error reading: %s\n", err->message);
1687 parse_cmd_line(ctl_line);
1694 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1695 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1696 if (unlink(uzbl.comm.fifo_path) == -1)
1697 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1698 g_free(uzbl.comm.fifo_path);
1699 uzbl.comm.fifo_path = NULL;
1702 if (*dir == ' ') { /* space unsets the variable */
1707 GIOChannel *chan = NULL;
1708 GError *error = NULL;
1709 gchar *path = build_stream_name(FIFO, dir);
1711 if (!file_exists(path)) {
1712 if (mkfifo (path, 0666) == 0) {
1713 // 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.
1714 chan = g_io_channel_new_file(path, "r+", &error);
1716 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1717 if (uzbl.state.verbose)
1718 printf ("init_fifo: created successfully as %s\n", path);
1719 uzbl.comm.fifo_path = path;
1721 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1722 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1723 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1724 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1726 /* if we got this far, there was an error; cleanup */
1727 if (error) g_error_free (error);
1734 control_stdin(GIOChannel *gio, GIOCondition condition) {
1736 gchar *ctl_line = NULL;
1739 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1740 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1743 parse_cmd_line(ctl_line);
1751 GIOChannel *chan = NULL;
1752 GError *error = NULL;
1754 chan = g_io_channel_unix_new(fileno(stdin));
1756 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1757 g_error ("Stdin: could not add watch\n");
1759 if (uzbl.state.verbose)
1760 printf ("Stdin: watch added successfully\n");
1763 g_error ("Stdin: Error while opening: %s\n", error->message);
1765 if (error) g_error_free (error);
1769 control_socket(GIOChannel *chan) {
1770 struct sockaddr_un remote;
1771 char buffer[512], *ctl_line;
1773 int sock, clientsock, n, done;
1776 sock = g_io_channel_unix_get_fd(chan);
1778 memset (buffer, 0, sizeof (buffer));
1780 t = sizeof (remote);
1781 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1785 memset (temp, 0, sizeof (temp));
1786 n = recv (clientsock, temp, 128, 0);
1788 buffer[strlen (buffer)] = '\0';
1792 strcat (buffer, temp);
1795 if (strcmp (buffer, "\n") < 0) {
1796 buffer[strlen (buffer) - 1] = '\0';
1798 buffer[strlen (buffer)] = '\0';
1801 ctl_line = g_strdup(buffer);
1802 parse_cmd_line (ctl_line);
1805 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1806 GError *error = NULL;
1809 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1810 if (ret == G_IO_STATUS_ERROR)
1811 g_error ("Error reading: %s\n", error->message);
1813 printf("Got line %s (%u bytes) \n",ctl_line, len);
1815 parse_line(ctl_line);
1823 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1824 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1825 if (unlink(uzbl.comm.socket_path) == -1)
1826 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1827 g_free(uzbl.comm.socket_path);
1828 uzbl.comm.socket_path = NULL;
1836 GIOChannel *chan = NULL;
1838 struct sockaddr_un local;
1839 gchar *path = build_stream_name(SOCKET, dir);
1841 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1843 local.sun_family = AF_UNIX;
1844 strcpy (local.sun_path, path);
1845 unlink (local.sun_path);
1847 len = strlen (local.sun_path) + sizeof (local.sun_family);
1848 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1849 if (uzbl.state.verbose)
1850 printf ("init_socket: opened in %s\n", path);
1853 if( (chan = g_io_channel_unix_new(sock)) ) {
1854 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1855 uzbl.comm.socket_path = path;
1858 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1860 /* if we got this far, there was an error; cleanup */
1867 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1868 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1870 // this function may be called very early when the templates are not set (yet), hence the checks
1872 update_title (void) {
1873 Behaviour *b = &uzbl.behave;
1876 if (b->show_status) {
1877 if (b->title_format_short) {
1878 parsed = expand_template(b->title_format_short, FALSE);
1879 if (uzbl.gui.main_window)
1880 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1883 if (b->status_format) {
1884 parsed = expand_template(b->status_format, TRUE);
1885 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1888 if (b->status_background) {
1890 gdk_color_parse (b->status_background, &color);
1891 //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)
1892 if (uzbl.gui.main_window)
1893 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1896 if (b->title_format_long) {
1897 parsed = expand_template(b->title_format_long, FALSE);
1898 if (uzbl.gui.main_window)
1899 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1906 key_press_cb (GtkWidget* window, GdkEventKey* event)
1908 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1912 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1913 || 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)
1916 /* turn off insert mode (if always_insert_mode is not used) */
1917 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1918 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1923 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1926 if (event->keyval == GDK_Escape) {
1927 g_string_truncate(uzbl.state.keycmd, 0);
1929 dehilight(uzbl.gui.web_view, NULL);
1933 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1934 if (event->keyval == GDK_Insert) {
1936 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1937 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1939 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1942 g_string_append (uzbl.state.keycmd, str);
1949 if (event->keyval == GDK_BackSpace)
1950 keycmd_bs(NULL, NULL);
1952 gboolean key_ret = FALSE;
1953 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1955 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1957 run_keycmd(key_ret);
1959 if (key_ret) return (!uzbl.behave.insert_mode);
1964 run_keycmd(const gboolean key_ret) {
1965 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1967 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1968 g_string_truncate(uzbl.state.keycmd, 0);
1969 parse_command(act->name, act->param);
1973 /* try if it's an incremental keycmd or one that takes args, and run it */
1974 GString* short_keys = g_string_new ("");
1975 GString* short_keys_inc = g_string_new ("");
1977 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1978 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1979 g_string_assign(short_keys_inc, short_keys->str);
1980 g_string_append_c(short_keys, '_');
1981 g_string_append_c(short_keys_inc, '*');
1983 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1984 /* run normal cmds only if return was pressed */
1985 exec_paramcmd(act, i);
1986 g_string_truncate(uzbl.state.keycmd, 0);
1988 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1989 if (key_ret) /* just quit the incremental command on return */
1990 g_string_truncate(uzbl.state.keycmd, 0);
1991 else exec_paramcmd(act, i); /* otherwise execute the incremental */
1995 g_string_truncate(short_keys, short_keys->len - 1);
1997 g_string_free (short_keys, TRUE);
1998 g_string_free (short_keys_inc, TRUE);
2002 exec_paramcmd(const Action *act, const guint i) {
2003 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2004 GString *actionname = g_string_new ("");
2005 GString *actionparam = g_string_new ("");
2006 g_string_erase (parampart, 0, i+1);
2008 g_string_printf (actionname, act->name, parampart->str);
2010 g_string_printf (actionparam, act->param, parampart->str);
2011 parse_command(actionname->str, actionparam->str);
2012 g_string_free(actionname, TRUE);
2013 g_string_free(actionparam, TRUE);
2014 g_string_free(parampart, TRUE);
2022 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2023 //main_window_ref = g_object_ref(scrolled_window);
2024 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
2026 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2027 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2029 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2030 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2031 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2032 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2033 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2034 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2035 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2036 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2037 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2038 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2039 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2041 return scrolled_window;
2048 g->mainbar = gtk_hbox_new (FALSE, 0);
2050 /* keep a reference to the bar so we can re-pack it at runtime*/
2051 //sbar_ref = g_object_ref(g->mainbar);
2053 g->mainbar_label = gtk_label_new ("");
2054 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2055 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2056 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2057 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2058 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2059 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2064 GtkWidget* create_window () {
2065 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2066 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2067 gtk_widget_set_name (window, "Uzbl browser");
2068 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2069 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2075 GtkPlug* create_plug () {
2076 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2077 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2078 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2085 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2087 If actname is one that calls an external command, this function will inject
2088 newargs in front of the user-provided args in that command line. They will
2089 come become after the body of the script (in sh) or after the name of
2090 the command to execute (in spawn).
2091 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2092 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2094 The return value consist of two strings: the action (sh, ...) and its args.
2096 If act is not one that calls an external command, then the given action merely
2099 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2100 gchar *actdup = g_strdup(actname);
2101 g_array_append_val(rets, actdup);
2103 if ((g_strcmp0(actname, "spawn") == 0) ||
2104 (g_strcmp0(actname, "sh") == 0) ||
2105 (g_strcmp0(actname, "sync_spawn") == 0) ||
2106 (g_strcmp0(actname, "sync_sh") == 0)) {
2108 GString *a = g_string_new("");
2109 gchar **spawnparts = split_quoted(origargs, FALSE);
2110 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2111 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2113 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2114 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2116 g_array_append_val(rets, a->str);
2117 g_string_free(a, FALSE);
2118 g_strfreev(spawnparts);
2120 gchar *origdup = g_strdup(origargs);
2121 g_array_append_val(rets, origdup);
2123 return (gchar**)g_array_free(rets, FALSE);
2127 run_handler (const gchar *act, const gchar *args) {
2128 /* Consider this code a temporary hack to make the handlers usable.
2129 In practice, all this splicing, injection, and reconstruction is
2130 inefficient, annoying and hard to manage. Potential pitfalls arise
2131 when the handler specific args 1) are not quoted (the handler
2132 callbacks should take care of this) 2) are quoted but interfere
2133 with the users' own quotation. A more ideal solution is
2134 to refactor parse_command so that it doesn't just take a string
2135 and execute it; rather than that, we should have a function which
2136 returns the argument vector parsed from the string. This vector
2137 could be modified (e.g. insert additional args into it) before
2138 passing it to the next function that actually executes it. Though
2139 it still isn't perfect for chain actions.. will reconsider & re-
2140 factor when I have the time. -duc */
2142 char **parts = g_strsplit(act, " ", 2);
2144 if (g_strcmp0(parts[0], "chain") == 0) {
2145 GString *newargs = g_string_new("");
2146 gchar **chainparts = split_quoted(parts[1], FALSE);
2148 /* for every argument in the chain, inject the handler args
2149 and make sure the new parts are wrapped in quotes */
2150 gchar **cp = chainparts;
2152 gchar *quotless = NULL;
2153 gchar **spliced_quotless = NULL; // sigh -_-;
2154 gchar **inpart = NULL;
2157 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2159 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2160 } else quotless = g_strdup(*cp);
2162 spliced_quotless = g_strsplit(quotless, " ", 2);
2163 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2164 g_strfreev(spliced_quotless);
2166 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2172 parse_command(parts[0], &(newargs->str[1]));
2173 g_string_free(newargs, TRUE);
2174 g_strfreev(chainparts);
2177 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2178 parse_command(inparts[0], inparts[1]);
2186 add_binding (const gchar *key, const gchar *act) {
2187 char **parts = g_strsplit(act, " ", 2);
2194 if (uzbl.state.verbose)
2195 printf ("Binding %-10s : %s\n", key, act);
2196 action = new_action(parts[0], parts[1]);
2198 if (g_hash_table_remove (uzbl.bindings, key))
2199 g_warning ("Overwriting existing binding for \"%s\"", key);
2200 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2205 get_xdg_var (XDG_Var xdg) {
2206 const gchar* actual_value = getenv (xdg.environmental);
2207 const gchar* home = getenv ("HOME");
2208 gchar* return_value;
2210 if (! actual_value || strcmp (actual_value, "") == 0) {
2211 if (xdg.default_value) {
2212 return_value = str_replace ("~", home, xdg.default_value);
2214 return_value = NULL;
2217 return_value = str_replace("~", home, actual_value);
2220 return return_value;
2224 find_xdg_file (int xdg_type, char* filename) {
2225 /* xdg_type = 0 => config
2226 xdg_type = 1 => data
2227 xdg_type = 2 => cache*/
2229 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2230 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2233 gchar* temporary_string;
2237 if (! file_exists (temporary_file) && xdg_type != 2) {
2238 buf = get_xdg_var (XDG[3 + xdg_type]);
2239 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2242 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2243 g_free (temporary_file);
2244 temporary_file = g_strconcat (temporary_string, filename, NULL);
2248 //g_free (temporary_string); - segfaults.
2250 if (file_exists (temporary_file)) {
2251 return temporary_file;
2258 State *s = &uzbl.state;
2259 Network *n = &uzbl.net;
2261 for (i = 0; default_config[i].command != NULL; i++) {
2262 parse_cmd_line(default_config[i].command);
2265 if (!s->config_file) {
2266 s->config_file = find_xdg_file (0, "/uzbl/config");
2269 if (s->config_file) {
2270 GArray* lines = read_file_by_line (s->config_file);
2274 while ((line = g_array_index(lines, gchar*, i))) {
2275 parse_cmd_line (line);
2279 g_array_free (lines, TRUE);
2281 if (uzbl.state.verbose)
2282 printf ("No configuration file loaded.\n");
2285 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2288 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2291 if (!uzbl.behave.cookie_handler)
2294 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2295 GString *s = g_string_new ("");
2296 SoupURI * soup_uri = soup_message_get_uri(msg);
2297 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2298 run_handler(uzbl.behave.cookie_handler, s->str);
2300 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2301 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2302 if ( p != NULL ) *p = '\0';
2303 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2305 if (uzbl.comm.sync_stdout)
2306 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2308 g_string_free(s, TRUE);
2312 save_cookies (SoupMessage *msg, gpointer user_data){
2316 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2317 cookie = soup_cookie_to_set_cookie_header(ck->data);
2318 SoupURI * soup_uri = soup_message_get_uri(msg);
2319 GString *s = g_string_new ("");
2320 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2321 run_handler(uzbl.behave.cookie_handler, s->str);
2323 g_string_free(s, TRUE);
2328 /* --- WEBINSPECTOR --- */
2330 hide_window_cb(GtkWidget *widget, gpointer data) {
2333 gtk_widget_hide(widget);
2336 static WebKitWebView*
2337 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2340 (void) web_inspector;
2341 GtkWidget* scrolled_window;
2342 GtkWidget* new_web_view;
2345 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2346 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2347 G_CALLBACK(hide_window_cb), NULL);
2349 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2350 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2351 gtk_widget_show(g->inspector_window);
2353 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2354 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2355 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2356 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2357 gtk_widget_show(scrolled_window);
2359 new_web_view = webkit_web_view_new();
2360 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2362 return WEBKIT_WEB_VIEW(new_web_view);
2366 inspector_show_window_cb (WebKitWebInspector* inspector){
2368 gtk_widget_show(uzbl.gui.inspector_window);
2372 /* TODO: Add variables and code to make use of these functions */
2374 inspector_close_window_cb (WebKitWebInspector* inspector){
2380 inspector_attach_window_cb (WebKitWebInspector* inspector){
2386 inspector_detach_window_cb (WebKitWebInspector* inspector){
2392 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2398 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2404 set_up_inspector() {
2406 WebKitWebSettings *settings = view_settings();
2407 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2409 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2410 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2411 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2412 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2413 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2414 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2415 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2417 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2421 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2423 uzbl_cmdprop *c = v;
2428 if(c->type == TYPE_STR)
2429 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2430 else if(c->type == TYPE_INT)
2431 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2435 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2439 printf("bind %s = %s %s\n", (char *)k ,
2440 (char *)a->name, a->param?(char *)a->param:"");
2445 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2446 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2451 main (int argc, char* argv[]) {
2452 gtk_init (&argc, &argv);
2453 if (!g_thread_supported ())
2454 g_thread_init (NULL);
2455 uzbl.state.executable_path = g_strdup(argv[0]);
2456 uzbl.state.selected_url = NULL;
2457 uzbl.state.searchtx = NULL;
2459 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2460 g_option_context_add_main_entries (context, entries, NULL);
2461 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2462 g_option_context_parse (context, &argc, &argv, NULL);
2463 g_option_context_free(context);
2465 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2466 gboolean verbose_override = uzbl.state.verbose;
2468 /* initialize hash table */
2469 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2471 uzbl.net.soup_session = webkit_get_default_session();
2472 uzbl.state.keycmd = g_string_new("");
2474 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2475 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2476 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2477 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2478 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2479 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2482 if(uname(&uzbl.state.unameinfo) == -1)
2483 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2485 uzbl.gui.sbar.progress_s = g_strdup("=");
2486 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2487 uzbl.gui.sbar.progress_w = 10;
2489 /* HTML mode defaults*/
2490 uzbl.behave.html_buffer = g_string_new("");
2491 uzbl.behave.html_endmarker = g_strdup(".");
2492 uzbl.behave.html_timeout = 60;
2493 uzbl.behave.base_url = g_strdup("http://invalid");
2495 /* default mode indicators */
2496 uzbl.behave.insert_indicator = g_strdup("I");
2497 uzbl.behave.cmd_indicator = g_strdup("C");
2501 make_var_to_name_hash();
2503 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2505 uzbl.gui.scrolled_win = create_browser();
2508 /* initial packing */
2509 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2510 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2512 if (uzbl.state.socket_id) {
2513 uzbl.gui.plug = create_plug ();
2514 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2515 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2517 uzbl.gui.main_window = create_window ();
2518 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2519 gtk_widget_show_all (uzbl.gui.main_window);
2520 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2523 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2525 if (uzbl.state.verbose) {
2526 printf("Uzbl start location: %s\n", argv[0]);
2527 if (uzbl.state.socket_id)
2528 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2530 printf("window_id %i\n",(int) uzbl.xwin);
2531 printf("pid %i\n", getpid ());
2532 printf("name: %s\n", uzbl.state.instance_name);
2535 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2536 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2537 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2538 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2539 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2543 if (!uzbl.behave.show_status)
2544 gtk_widget_hide(uzbl.gui.mainbar);
2553 if (verbose_override > uzbl.state.verbose)
2554 uzbl.state.verbose = verbose_override;
2557 set_var_value("uri", uri_override);
2558 g_free(uri_override);
2559 } else if (uzbl.state.uri)
2560 cmd_load_uri(uzbl.gui.web_view, NULL);
2565 return EXIT_SUCCESS;
2568 /* vi: set et ts=4: */