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>
63 /* commandline arguments (set initial values for the state variables) */
65 GOptionEntry entries[] =
67 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
68 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
69 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
70 "Whether to print all messages or just errors.", NULL },
71 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
72 "Name of the current instance (defaults to Xorg window id)", "NAME" },
73 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
74 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
75 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
76 "Socket ID", "SOCKET" },
77 { NULL, 0, 0, 0, NULL, NULL, NULL }
80 /* associate command names to their properties */
81 typedef const struct {
88 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
90 /* an abbreviation to help keep the table's width humane */
91 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
96 } var_name_to_ptr[] = {
97 /* variable name pointer to variable in code type dump callback function */
98 /* --------------------------------------------------------------------------------------- */
99 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
100 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
101 { "disable_stdin", PTR(uzbl.behave.disable_stdin, 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, GString *result) {
458 (void) page; (void) argv; (void) result;
459 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
463 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
464 (void) page; (void) argv; (void) result;
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, GString *result) {
471 (void) page; (void) result;
472 scroll(uzbl.gui.bar_v, argv);
476 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
477 (void) page; (void) result;
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, GString *result) {
497 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
501 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
506 if (uzbl.behave.show_status) {
507 gtk_widget_hide(uzbl.gui.mainbar);
509 gtk_widget_show(uzbl.gui.mainbar);
511 uzbl.behave.show_status = !uzbl.behave.show_status;
516 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
520 //Set selected_url state variable
521 g_free(uzbl.state.selected_url);
522 uzbl.state.selected_url = NULL;
524 uzbl.state.selected_url = g_strdup(link);
530 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
534 if (uzbl.gui.main_title)
535 g_free (uzbl.gui.main_title);
536 uzbl.gui.main_title = g_strdup (title);
541 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
544 uzbl.gui.sbar.load_progress = progress;
549 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
553 if (uzbl.behave.load_finish_handler)
554 run_handler(uzbl.behave.load_finish_handler, "");
558 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
562 uzbl.gui.sbar.load_progress = 0;
563 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
564 if (uzbl.behave.load_start_handler)
565 run_handler(uzbl.behave.load_start_handler, "");
569 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
572 g_free (uzbl.state.uri);
573 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
574 uzbl.state.uri = g_string_free (newuri, FALSE);
575 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
576 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
579 if (uzbl.behave.load_commit_handler)
580 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
584 destroy_cb (GtkWidget* widget, gpointer data) {
592 if (uzbl.behave.history_handler) {
594 struct tm * timeinfo;
597 timeinfo = localtime ( &rawtime );
598 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
599 run_handler(uzbl.behave.history_handler, date);
604 /* VIEW funcs (little webkit wrappers) */
605 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
607 VIEWFUNC(reload_bypass_cache)
608 VIEWFUNC(stop_loading)
615 /* -- command to callback/function map for things we cannot attach to any signals */
616 static struct {char *key; CommandInfo value;} cmdlist[] =
617 { /* key function no_split */
618 { "back", {view_go_back, 0} },
619 { "forward", {view_go_forward, 0} },
620 { "scroll_vert", {scroll_vert, 0} },
621 { "scroll_horz", {scroll_horz, 0} },
622 { "scroll_begin", {scroll_begin, 0} },
623 { "scroll_end", {scroll_end, 0} },
624 { "reload", {view_reload, 0}, },
625 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
626 { "stop", {view_stop_loading, 0}, },
627 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
628 { "zoom_out", {view_zoom_out, 0}, },
629 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
630 { "uri", {load_uri, TRUE} },
631 { "js", {run_js, TRUE} },
632 { "script", {run_external_js, 0} },
633 { "toggle_status", {toggle_status_cb, 0} },
634 { "spawn", {spawn, 0} },
635 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
636 { "sh", {spawn_sh, 0} },
637 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
638 { "exit", {close_uzbl, 0} },
639 { "search", {search_forward_text, TRUE} },
640 { "search_reverse", {search_reverse_text, TRUE} },
641 { "dehilight", {dehilight, 0} },
642 { "toggle_insert_mode", {toggle_insert_mode, 0} },
643 { "set", {set_var, TRUE} },
644 //{ "get", {get_var, TRUE} },
645 { "bind", {act_bind, TRUE} },
646 { "dump_config", {act_dump_config, 0} },
647 { "keycmd", {keycmd, TRUE} },
648 { "keycmd_nl", {keycmd_nl, TRUE} },
649 { "keycmd_bs", {keycmd_bs, 0} },
650 { "chain", {chain, 0} },
651 { "print", {print, TRUE} }
658 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
660 for (i = 0; i < LENGTH(cmdlist); i++)
661 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
664 /* -- CORE FUNCTIONS -- */
667 free_action(gpointer act) {
668 Action *action = (Action*)act;
669 g_free(action->name);
671 g_free(action->param);
676 new_action(const gchar *name, const gchar *param) {
677 Action *action = g_new(Action, 1);
679 action->name = g_strdup(name);
681 action->param = g_strdup(param);
683 action->param = NULL;
689 file_exists (const char * filename) {
690 return (access(filename, F_OK) == 0);
694 set_var(WebKitWebView *page, GArray *argv, GString *result) {
695 (void) page; (void) result;
696 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
697 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
698 set_var_value(g_strstrip(split[0]), value);
704 print(WebKitWebView *page, GArray *argv, GString *result) {
705 (void) page; (void) result;
708 buf = expand_vars(argv_idx(argv, 0));
709 puts(buf); /*TODO: result?*/
714 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
715 (void) page; (void) result;
716 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
717 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
718 add_binding(g_strstrip(split[0]), value);
730 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
731 (void) page; (void) result;
733 if (argv_idx(argv, 0)) {
734 if (strcmp (argv_idx(argv, 0), "0") == 0) {
735 uzbl.behave.insert_mode = FALSE;
737 uzbl.behave.insert_mode = TRUE;
740 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
747 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
750 if (argv_idx(argv, 0)) {
751 GString* newuri = g_string_new (argv_idx(argv, 0));
752 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
753 run_js(web_view, argv, NULL);
756 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
757 g_string_prepend (newuri, "http://");
758 /* if we do handle cookies, ask our handler for them */
759 webkit_web_view_load_uri (web_view, newuri->str);
760 g_string_free (newuri, TRUE);
765 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
768 if (argv_idx(argv, 0))
769 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
773 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
775 if (argv_idx(argv, 0)) {
776 GArray* lines = read_file_by_line (argv_idx (argv, 0));
781 while ((line = g_array_index(lines, gchar*, i))) {
783 js = g_strdup (line);
785 gchar* newjs = g_strconcat (js, line, NULL);
792 if (uzbl.state.verbose)
793 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
795 if (argv_idx (argv, 1)) {
796 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
800 webkit_web_view_execute_script (web_view, js);
802 g_array_free (lines, TRUE);
807 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
808 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
809 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
810 webkit_web_view_unmark_text_matches (page);
811 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
812 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
816 if (uzbl.state.searchtx) {
817 if (uzbl.state.verbose)
818 printf ("Searching: %s\n", uzbl.state.searchtx);
819 webkit_web_view_set_highlight_text_matches (page, TRUE);
820 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
825 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
827 search_text(page, argv, TRUE);
831 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
833 search_text(page, argv, FALSE);
837 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
838 (void) argv; (void) result;
839 webkit_web_view_set_highlight_text_matches (page, FALSE);
844 new_window_load_uri (const gchar * uri) {
845 GString* to_execute = g_string_new ("");
846 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
848 for (i = 0; entries[i].long_name != NULL; i++) {
849 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
850 gchar** str = (gchar**)entries[i].arg_data;
852 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
856 if (uzbl.state.verbose)
857 printf("\n%s\n", to_execute->str);
858 g_spawn_command_line_async (to_execute->str, NULL);
859 g_string_free (to_execute, TRUE);
863 chain (WebKitWebView *page, GArray *argv, GString *result) {
864 (void) page; (void) result;
866 gchar **parts = NULL;
868 while ((a = argv_idx(argv, i++))) {
869 parts = g_strsplit (a, " ", 2);
870 parse_command(parts[0], parts[1], result);
876 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
880 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
886 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
890 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
896 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
900 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
905 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
912 /* --Statusbar functions-- */
914 build_progressbar_ascii(int percent) {
915 int width=uzbl.gui.sbar.progress_w;
918 GString *bar = g_string_new("");
920 l = (double)percent*((double)width/100.);
921 l = (int)(l+.5)>=(int)l ? l+.5 : l;
923 for(i=0; i<(int)l; i++)
924 g_string_append(bar, uzbl.gui.sbar.progress_s);
927 g_string_append(bar, uzbl.gui.sbar.progress_u);
929 return g_string_free(bar, FALSE);
934 const GScannerConfig scan_config = {
937 ) /* cset_skip_characters */,
942 ) /* cset_identifier_first */,
949 ) /* cset_identifier_nth */,
950 ( "" ) /* cpair_comment_single */,
952 TRUE /* case_sensitive */,
954 FALSE /* skip_comment_multi */,
955 FALSE /* skip_comment_single */,
956 FALSE /* scan_comment_multi */,
957 TRUE /* scan_identifier */,
958 TRUE /* scan_identifier_1char */,
959 FALSE /* scan_identifier_NULL */,
960 TRUE /* scan_symbols */,
961 FALSE /* scan_binary */,
962 FALSE /* scan_octal */,
963 FALSE /* scan_float */,
964 FALSE /* scan_hex */,
965 FALSE /* scan_hex_dollar */,
966 FALSE /* scan_string_sq */,
967 FALSE /* scan_string_dq */,
968 TRUE /* numbers_2_int */,
969 FALSE /* int_2_float */,
970 FALSE /* identifier_2_string */,
971 FALSE /* char_2_token */,
972 FALSE /* symbol_2_token */,
973 TRUE /* scope_0_fallback */,
978 uzbl.scan = g_scanner_new(&scan_config);
979 while(symp->symbol_name) {
980 g_scanner_scope_add_symbol(uzbl.scan, 0,
982 GINT_TO_POINTER(symp->symbol_token));
988 expand_template(const char *template, gboolean escape_markup) {
989 if(!template) return NULL;
991 GTokenType token = G_TOKEN_NONE;
992 GString *ret = g_string_new("");
996 g_scanner_input_text(uzbl.scan, template, strlen(template));
997 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
998 token = g_scanner_get_next_token(uzbl.scan);
1000 if(token == G_TOKEN_SYMBOL) {
1001 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1005 buf = uzbl.state.uri?
1006 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1007 g_string_append(ret, buf);
1011 g_string_append(ret, uzbl.state.uri?
1012 uzbl.state.uri:g_strdup(""));
1015 buf = itos(uzbl.gui.sbar.load_progress);
1016 g_string_append(ret, buf);
1019 case SYM_LOADPRGSBAR:
1020 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1021 g_string_append(ret, buf);
1026 buf = uzbl.gui.main_title?
1027 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1028 g_string_append(ret, buf);
1032 g_string_append(ret, uzbl.gui.main_title?
1033 uzbl.gui.main_title:g_strdup(""));
1035 case SYM_SELECTED_URI:
1037 buf = uzbl.state.selected_url?
1038 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1039 g_string_append(ret, buf);
1043 g_string_append(ret, uzbl.state.selected_url?
1044 uzbl.state.selected_url:g_strdup(""));
1047 buf = itos(uzbl.xwin);
1048 g_string_append(ret,
1049 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1054 buf = uzbl.state.keycmd->str?
1055 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1056 g_string_append(ret, buf);
1060 g_string_append(ret, uzbl.state.keycmd->str?
1061 uzbl.state.keycmd->str:g_strdup(""));
1064 g_string_append(ret,
1065 uzbl.behave.insert_mode?
1066 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1069 g_string_append(ret,
1070 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1072 /* useragent syms */
1074 buf = itos(WEBKIT_MAJOR_VERSION);
1075 g_string_append(ret, buf);
1079 buf = itos(WEBKIT_MINOR_VERSION);
1080 g_string_append(ret, buf);
1084 buf = itos(WEBKIT_MICRO_VERSION);
1085 g_string_append(ret, buf);
1089 g_string_append(ret, uzbl.state.unameinfo.sysname);
1092 g_string_append(ret, uzbl.state.unameinfo.nodename);
1095 g_string_append(ret, uzbl.state.unameinfo.release);
1098 g_string_append(ret, uzbl.state.unameinfo.version);
1101 g_string_append(ret, uzbl.state.unameinfo.machine);
1104 g_string_append(ret, ARCH);
1107 case SYM_DOMAINNAME:
1108 g_string_append(ret, uzbl.state.unameinfo.domainname);
1112 g_string_append(ret, COMMIT);
1118 else if(token == G_TOKEN_INT) {
1119 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1120 g_string_append(ret, buf);
1123 else if(token == G_TOKEN_IDENTIFIER) {
1124 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1126 else if(token == G_TOKEN_CHAR) {
1127 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1131 return g_string_free(ret, FALSE);
1133 /* --End Statusbar functions-- */
1136 sharg_append(GArray *a, const gchar *str) {
1137 const gchar *s = (str ? str : "");
1138 g_array_append_val(a, s);
1141 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1143 run_command (const gchar *command, const guint npre, const gchar **args,
1144 const gboolean sync, char **output_stdout) {
1145 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1148 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1149 gchar *pid = itos(getpid());
1150 gchar *xwin = itos(uzbl.xwin);
1152 sharg_append(a, command);
1153 for (i = 0; i < npre; i++) /* add n args before the default vars */
1154 sharg_append(a, args[i]);
1155 sharg_append(a, uzbl.state.config_file);
1156 sharg_append(a, pid);
1157 sharg_append(a, xwin);
1158 sharg_append(a, uzbl.comm.fifo_path);
1159 sharg_append(a, uzbl.comm.socket_path);
1160 sharg_append(a, uzbl.state.uri);
1161 sharg_append(a, uzbl.gui.main_title);
1163 for (i = npre; i < g_strv_length((gchar**)args); i++)
1164 sharg_append(a, args[i]);
1168 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1170 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1171 NULL, NULL, output_stdout, NULL, NULL, &err);
1172 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1173 NULL, NULL, NULL, &err);
1175 if (uzbl.state.verbose) {
1176 GString *s = g_string_new("spawned:");
1177 for (i = 0; i < (a->len); i++) {
1178 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1179 g_string_append_printf(s, " %s", qarg);
1182 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1183 printf("%s\n", s->str);
1184 g_string_free(s, TRUE);
1186 printf("Stdout: %s\n", *output_stdout);
1190 g_printerr("error on run_command: %s\n", err->message);
1195 g_array_free (a, TRUE);
1200 split_quoted(const gchar* src, const gboolean unquote) {
1201 /* split on unquoted space, return array of strings;
1202 remove a layer of quotes and backslashes if unquote */
1203 if (!src) return NULL;
1205 gboolean dq = FALSE;
1206 gboolean sq = FALSE;
1207 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1208 GString *s = g_string_new ("");
1212 for (p = src; *p != '\0'; p++) {
1213 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1214 else if (*p == '\\') { g_string_append_c(s, *p++);
1215 g_string_append_c(s, *p); }
1216 else if ((*p == '"') && unquote && !sq) dq = !dq;
1217 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1219 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1220 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1222 else if ((*p == ' ') && !dq && !sq) {
1223 dup = g_strdup(s->str);
1224 g_array_append_val(a, dup);
1225 g_string_truncate(s, 0);
1226 } else g_string_append_c(s, *p);
1228 dup = g_strdup(s->str);
1229 g_array_append_val(a, dup);
1230 ret = (gchar**)a->data;
1231 g_array_free (a, FALSE);
1232 g_string_free (s, TRUE);
1237 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1238 (void)web_view; (void)result;
1239 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1240 if (argv_idx(argv, 0))
1241 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1245 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1246 (void)web_view; (void)result;
1248 if (argv_idx(argv, 0))
1249 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1250 TRUE, &uzbl.comm.sync_stdout);
1254 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1255 (void)web_view; (void)result;
1256 if (!uzbl.behave.shell_cmd) {
1257 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1262 gchar *spacer = g_strdup("");
1263 g_array_insert_val(argv, 1, spacer);
1264 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1266 for (i = 1; i < g_strv_length(cmd); i++)
1267 g_array_prepend_val(argv, cmd[i]);
1269 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1275 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1276 (void)web_view; (void)result;
1277 if (!uzbl.behave.shell_cmd) {
1278 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1283 gchar *spacer = g_strdup("");
1284 g_array_insert_val(argv, 1, spacer);
1285 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1287 for (i = 1; i < g_strv_length(cmd); i++)
1288 g_array_prepend_val(argv, cmd[i]);
1290 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1291 TRUE, &uzbl.comm.sync_stdout);
1297 parse_command(const char *cmd, const char *param, GString *result) {
1300 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1302 gchar **par = split_quoted(param, TRUE);
1303 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1305 if (c->no_split) { /* don't split */
1306 sharg_append(a, param);
1308 for (i = 0; i < g_strv_length(par); i++)
1309 sharg_append(a, par[i]);
1312 if (result == NULL) {
1313 GString *result_print = g_string_new("");
1315 c->function(uzbl.gui.web_view, a, result_print);
1316 if (uzbl.state.verbose)
1317 printf("%s returned %s\n", cmd, result_print->str);
1319 g_string_free(result_print, TRUE);
1321 c->function(uzbl.gui.web_view, a, result);
1324 g_array_free (a, TRUE);
1327 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1334 if(*uzbl.net.proxy_url == ' '
1335 || uzbl.net.proxy_url == NULL) {
1336 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1337 (GType) SOUP_SESSION_PROXY_URI);
1340 suri = soup_uri_new(uzbl.net.proxy_url);
1341 g_object_set(G_OBJECT(uzbl.net.soup_session),
1342 SOUP_SESSION_PROXY_URI,
1344 soup_uri_free(suri);
1351 if(file_exists(uzbl.gui.icon)) {
1352 if (uzbl.gui.main_window)
1353 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1355 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1357 g_free (uzbl.gui.icon);
1362 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1363 g_array_append_val (a, uzbl.state.uri);
1364 load_uri(uzbl.gui.web_view, a, NULL);
1365 g_array_free (a, TRUE);
1369 cmd_always_insert_mode() {
1370 uzbl.behave.insert_mode =
1371 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1377 g_object_set(G_OBJECT(uzbl.net.soup_session),
1378 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1382 cmd_max_conns_host() {
1383 g_object_set(G_OBJECT(uzbl.net.soup_session),
1384 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1389 soup_session_remove_feature
1390 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1391 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1392 /*g_free(uzbl.net.soup_logger);*/
1394 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1395 soup_session_add_feature(uzbl.net.soup_session,
1396 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1399 static WebKitWebSettings*
1401 return webkit_web_view_get_settings(uzbl.gui.web_view);
1406 WebKitWebSettings *ws = view_settings();
1407 if (uzbl.behave.font_size > 0) {
1408 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1411 if (uzbl.behave.monospace_size > 0) {
1412 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1413 uzbl.behave.monospace_size, NULL);
1415 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1416 uzbl.behave.font_size, NULL);
1422 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1426 cmd_disable_plugins() {
1427 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1428 !uzbl.behave.disable_plugins, NULL);
1432 cmd_disable_scripts() {
1433 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1434 !uzbl.behave.disable_scripts, NULL);
1438 cmd_minimum_font_size() {
1439 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1440 uzbl.behave.minimum_font_size, NULL);
1443 cmd_autoload_img() {
1444 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1445 uzbl.behave.autoload_img, NULL);
1450 cmd_autoshrink_img() {
1451 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1452 uzbl.behave.autoshrink_img, NULL);
1457 cmd_enable_spellcheck() {
1458 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1459 uzbl.behave.enable_spellcheck, NULL);
1463 cmd_enable_private() {
1464 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1465 uzbl.behave.enable_private, NULL);
1470 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1471 uzbl.behave.print_bg, NULL);
1476 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1477 uzbl.behave.style_uri, NULL);
1481 cmd_resizable_txt() {
1482 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1483 uzbl.behave.resizable_txt, NULL);
1487 cmd_default_encoding() {
1488 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1489 uzbl.behave.default_encoding, NULL);
1493 cmd_enforce_96dpi() {
1494 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1495 uzbl.behave.enforce_96dpi, NULL);
1499 cmd_caret_browsing() {
1500 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1501 uzbl.behave.caret_browsing, NULL);
1505 cmd_cookie_handler() {
1506 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1507 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1508 if ((g_strcmp0(split[0], "sh") == 0) ||
1509 (g_strcmp0(split[0], "spawn") == 0)) {
1510 g_free (uzbl.behave.cookie_handler);
1511 uzbl.behave.cookie_handler =
1512 g_strdup_printf("sync_%s %s", split[0], split[1]);
1519 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1524 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1529 if(uzbl.behave.inject_html) {
1530 webkit_web_view_load_html_string (uzbl.gui.web_view,
1531 uzbl.behave.inject_html, NULL);
1540 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1541 uzbl.behave.modmask = 0;
1543 if(uzbl.behave.modkey)
1544 g_free(uzbl.behave.modkey);
1545 uzbl.behave.modkey = buf;
1547 for (i = 0; modkeys[i].key != NULL; i++) {
1548 if (g_strrstr(buf, modkeys[i].key))
1549 uzbl.behave.modmask |= modkeys[i].mask;
1555 if (*uzbl.net.useragent == ' ') {
1556 g_free (uzbl.net.useragent);
1557 uzbl.net.useragent = NULL;
1559 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1561 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1562 g_free(uzbl.net.useragent);
1563 uzbl.net.useragent = ua;
1569 gtk_widget_ref(uzbl.gui.scrolled_win);
1570 gtk_widget_ref(uzbl.gui.mainbar);
1571 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1572 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1574 if(uzbl.behave.status_top) {
1575 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1576 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1579 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1580 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1582 gtk_widget_unref(uzbl.gui.scrolled_win);
1583 gtk_widget_unref(uzbl.gui.mainbar);
1584 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1589 set_var_value(gchar *name, gchar *val) {
1590 uzbl_cmdprop *c = NULL;
1594 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1595 /* check for the variable type */
1596 if (c->type == TYPE_STR) {
1597 buf = expand_vars(val);
1600 } else if(c->type == TYPE_INT) {
1601 int *ip = (int *)c->ptr;
1602 buf = expand_vars(val);
1603 *ip = (int)strtoul(buf, &endp, 10);
1605 } else if (c->type == TYPE_FLOAT) {
1606 float *fp = (float *)c->ptr;
1607 buf = expand_vars(val);
1608 *fp = strtod(buf, &endp);
1612 /* invoke a command specific function */
1613 if(c->func) c->func();
1620 Behaviour *b = &uzbl.behave;
1622 if(b->html_buffer->str) {
1623 webkit_web_view_load_html_string (uzbl.gui.web_view,
1624 b->html_buffer->str, b->base_url);
1625 g_string_free(b->html_buffer, TRUE);
1626 b->html_buffer = g_string_new("");
1630 enum {M_CMD, M_HTML};
1632 parse_cmd_line(const char *ctl_line, GString *result) {
1633 Behaviour *b = &uzbl.behave;
1636 if(b->mode == M_HTML) {
1637 len = strlen(b->html_endmarker);
1638 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1639 if(len == strlen(ctl_line)-1 &&
1640 !strncmp(b->html_endmarker, ctl_line, len)) {
1642 set_var_value("mode", "0");
1647 set_timeout(b->html_timeout);
1648 g_string_append(b->html_buffer, ctl_line);
1651 else if((ctl_line[0] == '#') /* Comments */
1652 || (ctl_line[0] == ' ')
1653 || (ctl_line[0] == '\n'))
1654 ; /* ignore these lines */
1655 else { /* parse a command */
1657 gchar **tokens = NULL;
1658 len = strlen(ctl_line);
1660 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1661 ctlstrip = g_strndup(ctl_line, len - 1);
1662 else ctlstrip = g_strdup(ctl_line);
1664 tokens = g_strsplit(ctlstrip, " ", 2);
1665 parse_command(tokens[0], tokens[1], result);
1672 build_stream_name(int type, const gchar* dir) {
1674 State *s = &uzbl.state;
1677 xwin_str = itos((int)uzbl.xwin);
1679 str = g_strdup_printf
1680 ("%s/uzbl_fifo_%s", dir,
1681 s->instance_name ? s->instance_name : xwin_str);
1682 } else if (type == SOCKET) {
1683 str = g_strdup_printf
1684 ("%s/uzbl_socket_%s", dir,
1685 s->instance_name ? s->instance_name : xwin_str );
1692 control_fifo(GIOChannel *gio, GIOCondition condition) {
1693 if (uzbl.state.verbose)
1694 printf("triggered\n");
1699 if (condition & G_IO_HUP)
1700 g_error ("Fifo: Read end of pipe died!\n");
1703 g_error ("Fifo: GIOChannel broke\n");
1705 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1706 if (ret == G_IO_STATUS_ERROR) {
1707 g_error ("Fifo: Error reading: %s\n", err->message);
1711 parse_cmd_line(ctl_line, NULL);
1718 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1719 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1720 if (unlink(uzbl.comm.fifo_path) == -1)
1721 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1722 g_free(uzbl.comm.fifo_path);
1723 uzbl.comm.fifo_path = NULL;
1726 if (*dir == ' ') { /* space unsets the variable */
1731 GIOChannel *chan = NULL;
1732 GError *error = NULL;
1733 gchar *path = build_stream_name(FIFO, dir);
1735 if (!file_exists(path)) {
1736 if (mkfifo (path, 0666) == 0) {
1737 // 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.
1738 chan = g_io_channel_new_file(path, "r+", &error);
1740 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1741 if (uzbl.state.verbose)
1742 printf ("init_fifo: created successfully as %s\n", path);
1743 uzbl.comm.fifo_path = path;
1745 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1746 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1747 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1748 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1750 /* if we got this far, there was an error; cleanup */
1751 if (error) g_error_free (error);
1758 control_stdin(GIOChannel *gio, GIOCondition condition) {
1760 gchar *ctl_line = NULL;
1763 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1764 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1767 parse_cmd_line(ctl_line, NULL);
1775 GIOChannel *chan = NULL;
1776 GError *error = NULL;
1778 chan = g_io_channel_unix_new(fileno(stdin));
1780 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1781 g_error ("Stdin: could not add watch\n");
1783 if (uzbl.state.verbose)
1784 printf ("Stdin: watch added successfully\n");
1787 g_error ("Stdin: Error while opening: %s\n", error->message);
1789 if (error) g_error_free (error);
1793 control_socket(GIOChannel *chan) {
1794 struct sockaddr_un remote;
1795 unsigned int t = sizeof(remote);
1797 GIOChannel *clientchan;
1799 clientsock = accept (g_io_channel_unix_get_fd(chan),
1800 (struct sockaddr *) &remote, &t);
1802 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1803 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1804 (GIOFunc) control_client_socket, clientchan);
1811 control_client_socket(GIOChannel *clientchan) {
1813 GString *result = g_string_new("");
1814 GError *error = NULL;
1818 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1819 if (ret == G_IO_STATUS_ERROR) {
1820 g_error ("Error reading: %s\n", error->message);
1822 } else if (ret == G_IO_STATUS_EOF) {
1823 /* socket closed, remove channel watch from main loop */
1828 parse_cmd_line (ctl_line, result);
1829 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
1831 if (ret == G_IO_STATUS_ERROR) {
1832 g_error ("Error writing: %s", error->message);
1836 g_string_free(result, TRUE);
1842 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1843 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1844 if (unlink(uzbl.comm.socket_path) == -1)
1845 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1846 g_free(uzbl.comm.socket_path);
1847 uzbl.comm.socket_path = NULL;
1855 GIOChannel *chan = NULL;
1857 struct sockaddr_un local;
1858 gchar *path = build_stream_name(SOCKET, dir);
1860 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1862 local.sun_family = AF_UNIX;
1863 strcpy (local.sun_path, path);
1864 unlink (local.sun_path);
1866 len = strlen (local.sun_path) + sizeof (local.sun_family);
1867 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1868 if (uzbl.state.verbose)
1869 printf ("init_socket: opened in %s\n", path);
1872 if( (chan = g_io_channel_unix_new(sock)) ) {
1873 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1874 uzbl.comm.socket_path = path;
1877 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1879 /* if we got this far, there was an error; cleanup */
1886 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1887 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1889 // this function may be called very early when the templates are not set (yet), hence the checks
1891 update_title (void) {
1892 Behaviour *b = &uzbl.behave;
1895 if (b->show_status) {
1896 if (b->title_format_short) {
1897 parsed = expand_template(b->title_format_short, FALSE);
1898 if (uzbl.gui.main_window)
1899 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1902 if (b->status_format) {
1903 parsed = expand_template(b->status_format, TRUE);
1904 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1907 if (b->status_background) {
1909 gdk_color_parse (b->status_background, &color);
1910 //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)
1911 if (uzbl.gui.main_window)
1912 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1915 if (b->title_format_long) {
1916 parsed = expand_template(b->title_format_long, FALSE);
1917 if (uzbl.gui.main_window)
1918 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1925 key_press_cb (GtkWidget* window, GdkEventKey* event)
1927 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1931 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1932 || 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)
1935 /* turn off insert mode (if always_insert_mode is not used) */
1936 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1937 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1942 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1945 if (event->keyval == GDK_Escape) {
1946 g_string_truncate(uzbl.state.keycmd, 0);
1948 dehilight(uzbl.gui.web_view, NULL, NULL);
1952 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1953 if (event->keyval == GDK_Insert) {
1955 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1956 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1958 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1961 g_string_append (uzbl.state.keycmd, str);
1968 if (event->keyval == GDK_BackSpace)
1969 keycmd_bs(NULL, NULL, NULL);
1971 gboolean key_ret = FALSE;
1972 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1974 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1976 run_keycmd(key_ret);
1978 if (key_ret) return (!uzbl.behave.insert_mode);
1983 run_keycmd(const gboolean key_ret) {
1984 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1986 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1987 g_string_truncate(uzbl.state.keycmd, 0);
1988 parse_command(act->name, act->param, NULL);
1992 /* try if it's an incremental keycmd or one that takes args, and run it */
1993 GString* short_keys = g_string_new ("");
1994 GString* short_keys_inc = g_string_new ("");
1996 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1997 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1998 g_string_assign(short_keys_inc, short_keys->str);
1999 g_string_append_c(short_keys, '_');
2000 g_string_append_c(short_keys_inc, '*');
2002 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2003 /* run normal cmds only if return was pressed */
2004 exec_paramcmd(act, i);
2005 g_string_truncate(uzbl.state.keycmd, 0);
2007 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2008 if (key_ret) /* just quit the incremental command on return */
2009 g_string_truncate(uzbl.state.keycmd, 0);
2010 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2014 g_string_truncate(short_keys, short_keys->len - 1);
2016 g_string_free (short_keys, TRUE);
2017 g_string_free (short_keys_inc, TRUE);
2021 exec_paramcmd(const Action *act, const guint i) {
2022 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2023 GString *actionname = g_string_new ("");
2024 GString *actionparam = g_string_new ("");
2025 g_string_erase (parampart, 0, i+1);
2027 g_string_printf (actionname, act->name, parampart->str);
2029 g_string_printf (actionparam, act->param, parampart->str);
2030 parse_command(actionname->str, actionparam->str, NULL);
2031 g_string_free(actionname, TRUE);
2032 g_string_free(actionparam, TRUE);
2033 g_string_free(parampart, TRUE);
2041 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2042 //main_window_ref = g_object_ref(scrolled_window);
2043 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
2045 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2046 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2048 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2049 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2050 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2051 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2052 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2053 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2054 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2055 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2056 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2057 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2058 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2060 return scrolled_window;
2067 g->mainbar = gtk_hbox_new (FALSE, 0);
2069 /* keep a reference to the bar so we can re-pack it at runtime*/
2070 //sbar_ref = g_object_ref(g->mainbar);
2072 g->mainbar_label = gtk_label_new ("");
2073 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2074 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2075 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2076 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2077 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2078 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2083 GtkWidget* create_window () {
2084 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2085 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2086 gtk_widget_set_name (window, "Uzbl browser");
2087 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2088 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2094 GtkPlug* create_plug () {
2095 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2096 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2097 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2104 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2106 If actname is one that calls an external command, this function will inject
2107 newargs in front of the user-provided args in that command line. They will
2108 come become after the body of the script (in sh) or after the name of
2109 the command to execute (in spawn).
2110 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2111 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2113 The return value consist of two strings: the action (sh, ...) and its args.
2115 If act is not one that calls an external command, then the given action merely
2118 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2119 gchar *actdup = g_strdup(actname);
2120 g_array_append_val(rets, actdup);
2122 if ((g_strcmp0(actname, "spawn") == 0) ||
2123 (g_strcmp0(actname, "sh") == 0) ||
2124 (g_strcmp0(actname, "sync_spawn") == 0) ||
2125 (g_strcmp0(actname, "sync_sh") == 0)) {
2127 GString *a = g_string_new("");
2128 gchar **spawnparts = split_quoted(origargs, FALSE);
2129 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2130 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2132 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2133 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2135 g_array_append_val(rets, a->str);
2136 g_string_free(a, FALSE);
2137 g_strfreev(spawnparts);
2139 gchar *origdup = g_strdup(origargs);
2140 g_array_append_val(rets, origdup);
2142 return (gchar**)g_array_free(rets, FALSE);
2146 run_handler (const gchar *act, const gchar *args) {
2147 /* Consider this code a temporary hack to make the handlers usable.
2148 In practice, all this splicing, injection, and reconstruction is
2149 inefficient, annoying and hard to manage. Potential pitfalls arise
2150 when the handler specific args 1) are not quoted (the handler
2151 callbacks should take care of this) 2) are quoted but interfere
2152 with the users' own quotation. A more ideal solution is
2153 to refactor parse_command so that it doesn't just take a string
2154 and execute it; rather than that, we should have a function which
2155 returns the argument vector parsed from the string. This vector
2156 could be modified (e.g. insert additional args into it) before
2157 passing it to the next function that actually executes it. Though
2158 it still isn't perfect for chain actions.. will reconsider & re-
2159 factor when I have the time. -duc */
2161 char **parts = g_strsplit(act, " ", 2);
2163 if (g_strcmp0(parts[0], "chain") == 0) {
2164 GString *newargs = g_string_new("");
2165 gchar **chainparts = split_quoted(parts[1], FALSE);
2167 /* for every argument in the chain, inject the handler args
2168 and make sure the new parts are wrapped in quotes */
2169 gchar **cp = chainparts;
2171 gchar *quotless = NULL;
2172 gchar **spliced_quotless = NULL; // sigh -_-;
2173 gchar **inpart = NULL;
2176 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2178 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2179 } else quotless = g_strdup(*cp);
2181 spliced_quotless = g_strsplit(quotless, " ", 2);
2182 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2183 g_strfreev(spliced_quotless);
2185 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2191 parse_command(parts[0], &(newargs->str[1]), NULL);
2192 g_string_free(newargs, TRUE);
2193 g_strfreev(chainparts);
2196 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2197 parse_command(inparts[0], inparts[1], NULL);
2205 add_binding (const gchar *key, const gchar *act) {
2206 char **parts = g_strsplit(act, " ", 2);
2213 if (uzbl.state.verbose)
2214 printf ("Binding %-10s : %s\n", key, act);
2215 action = new_action(parts[0], parts[1]);
2217 if (g_hash_table_remove (uzbl.bindings, key))
2218 g_warning ("Overwriting existing binding for \"%s\"", key);
2219 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2224 get_xdg_var (XDG_Var xdg) {
2225 const gchar* actual_value = getenv (xdg.environmental);
2226 const gchar* home = getenv ("HOME");
2227 gchar* return_value;
2229 if (! actual_value || strcmp (actual_value, "") == 0) {
2230 if (xdg.default_value) {
2231 return_value = str_replace ("~", home, xdg.default_value);
2233 return_value = NULL;
2236 return_value = str_replace("~", home, actual_value);
2239 return return_value;
2243 find_xdg_file (int xdg_type, char* filename) {
2244 /* xdg_type = 0 => config
2245 xdg_type = 1 => data
2246 xdg_type = 2 => cache*/
2248 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2249 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2252 gchar* temporary_string;
2256 if (! file_exists (temporary_file) && xdg_type != 2) {
2257 buf = get_xdg_var (XDG[3 + xdg_type]);
2258 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2261 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2262 g_free (temporary_file);
2263 temporary_file = g_strconcat (temporary_string, filename, NULL);
2267 //g_free (temporary_string); - segfaults.
2269 if (file_exists (temporary_file)) {
2270 return temporary_file;
2277 State *s = &uzbl.state;
2278 Network *n = &uzbl.net;
2280 for (i = 0; default_config[i].command != NULL; i++) {
2281 parse_cmd_line(default_config[i].command, NULL);
2284 if (!s->config_file) {
2285 s->config_file = find_xdg_file (0, "/uzbl/config");
2288 if (s->config_file) {
2289 GArray* lines = read_file_by_line (s->config_file);
2293 while ((line = g_array_index(lines, gchar*, i))) {
2294 parse_cmd_line (line, NULL);
2298 g_array_free (lines, TRUE);
2300 if (uzbl.state.verbose)
2301 printf ("No configuration file loaded.\n");
2304 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2307 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2310 if (!uzbl.behave.cookie_handler)
2313 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2314 GString *s = g_string_new ("");
2315 SoupURI * soup_uri = soup_message_get_uri(msg);
2316 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2317 run_handler(uzbl.behave.cookie_handler, s->str);
2319 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2320 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2321 if ( p != NULL ) *p = '\0';
2322 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2324 if (uzbl.comm.sync_stdout)
2325 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2327 g_string_free(s, TRUE);
2331 save_cookies (SoupMessage *msg, gpointer user_data){
2335 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2336 cookie = soup_cookie_to_set_cookie_header(ck->data);
2337 SoupURI * soup_uri = soup_message_get_uri(msg);
2338 GString *s = g_string_new ("");
2339 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2340 run_handler(uzbl.behave.cookie_handler, s->str);
2342 g_string_free(s, TRUE);
2347 /* --- WEBINSPECTOR --- */
2349 hide_window_cb(GtkWidget *widget, gpointer data) {
2352 gtk_widget_hide(widget);
2355 static WebKitWebView*
2356 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2359 (void) web_inspector;
2360 GtkWidget* scrolled_window;
2361 GtkWidget* new_web_view;
2364 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2365 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2366 G_CALLBACK(hide_window_cb), NULL);
2368 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2369 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2370 gtk_widget_show(g->inspector_window);
2372 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2373 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2374 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2375 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2376 gtk_widget_show(scrolled_window);
2378 new_web_view = webkit_web_view_new();
2379 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2381 return WEBKIT_WEB_VIEW(new_web_view);
2385 inspector_show_window_cb (WebKitWebInspector* inspector){
2387 gtk_widget_show(uzbl.gui.inspector_window);
2391 /* TODO: Add variables and code to make use of these functions */
2393 inspector_close_window_cb (WebKitWebInspector* inspector){
2399 inspector_attach_window_cb (WebKitWebInspector* inspector){
2405 inspector_detach_window_cb (WebKitWebInspector* inspector){
2411 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2417 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2423 set_up_inspector() {
2425 WebKitWebSettings *settings = view_settings();
2426 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2428 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2429 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2430 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2431 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2432 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2433 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2434 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2436 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2440 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2442 uzbl_cmdprop *c = v;
2447 if(c->type == TYPE_STR)
2448 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2449 else if(c->type == TYPE_INT)
2450 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2454 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2458 printf("bind %s = %s %s\n", (char *)k ,
2459 (char *)a->name, a->param?(char *)a->param:"");
2464 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2465 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2470 main (int argc, char* argv[]) {
2471 gtk_init (&argc, &argv);
2472 if (!g_thread_supported ())
2473 g_thread_init (NULL);
2474 uzbl.state.executable_path = g_strdup(argv[0]);
2475 uzbl.state.selected_url = NULL;
2476 uzbl.state.searchtx = NULL;
2478 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2479 g_option_context_add_main_entries (context, entries, NULL);
2480 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2481 g_option_context_parse (context, &argc, &argv, NULL);
2482 g_option_context_free(context);
2484 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2485 gboolean verbose_override = uzbl.state.verbose;
2487 /* initialize hash table */
2488 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2490 uzbl.net.soup_session = webkit_get_default_session();
2491 uzbl.state.keycmd = g_string_new("");
2493 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2494 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2495 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2496 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2497 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2498 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2501 if(uname(&uzbl.state.unameinfo) == -1)
2502 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2504 uzbl.gui.sbar.progress_s = g_strdup("=");
2505 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2506 uzbl.gui.sbar.progress_w = 10;
2508 /* HTML mode defaults*/
2509 uzbl.behave.html_buffer = g_string_new("");
2510 uzbl.behave.html_endmarker = g_strdup(".");
2511 uzbl.behave.html_timeout = 60;
2512 uzbl.behave.base_url = g_strdup("http://invalid");
2514 /* default mode indicators */
2515 uzbl.behave.insert_indicator = g_strdup("I");
2516 uzbl.behave.cmd_indicator = g_strdup("C");
2520 make_var_to_name_hash();
2522 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2524 uzbl.gui.scrolled_win = create_browser();
2527 /* initial packing */
2528 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2529 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2531 if (uzbl.state.socket_id) {
2532 uzbl.gui.plug = create_plug ();
2533 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2534 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2536 uzbl.gui.main_window = create_window ();
2537 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2538 gtk_widget_show_all (uzbl.gui.main_window);
2539 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2542 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2544 if (uzbl.state.verbose) {
2545 printf("Uzbl start location: %s\n", argv[0]);
2546 if (uzbl.state.socket_id)
2547 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2549 printf("window_id %i\n",(int) uzbl.xwin);
2550 printf("pid %i\n", getpid ());
2551 printf("name: %s\n", uzbl.state.instance_name);
2554 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2555 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2556 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2557 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2558 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2562 if (!uzbl.behave.show_status)
2563 gtk_widget_hide(uzbl.gui.mainbar);
2570 if (!uzbl.behave.disable_stdin)
2573 if (verbose_override > uzbl.state.verbose)
2574 uzbl.state.verbose = verbose_override;
2577 set_var_value("uri", uri_override);
2578 g_free(uri_override);
2579 } else if (uzbl.state.uri)
2580 cmd_load_uri(uzbl.gui.web_view, NULL);
2585 return EXIT_SUCCESS;
2588 /* vi: set et ts=4: */