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>
48 #include <JavaScriptCore/JavaScript.h>
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 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
197 get_exp_type(gchar *s) {
201 else if(*(s+1) == '{')
202 return EXP_BRACED_VAR;
203 else if(*(s+1) == '<')
206 return EXP_SIMPLE_VAR;
211 /* setting 'recurse = 1' will prevent expand() from
212 * expanding '@(command)@' and '@<js>@'
215 expand(char *s, gboolean recurse) {
219 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
224 gchar *cmd_stdout = NULL;
226 GString *buf = g_string_new("");
227 GString *js_ret = g_string_new("");
232 g_string_append_c(buf, *++s);
237 etype = get_exp_type(s);
242 if( (vend = strpbrk(s, end_simple_var)) ||
243 (vend = strchr(s, '\0')) ) {
244 strncpy(ret, s, vend-s);
250 if( (vend = strchr(s, upto)) ||
251 (vend = strchr(s, '\0')) ) {
252 strncpy(ret, s, vend-s);
258 strcpy(str_end, ")@");
260 if( (vend = strstr(s, str_end)) ||
261 (vend = strchr(s, '\0')) ) {
262 strncpy(ret, s, vend-s);
268 strcpy(str_end, ">@");
270 if( (vend = strstr(s, str_end)) ||
271 (vend = strchr(s, '\0')) ) {
272 strncpy(ret, s, vend-s);
278 if(etype == EXP_SIMPLE_VAR ||
279 etype == EXP_BRACED_VAR) {
280 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
281 if(c->type == TYPE_STR)
282 g_string_append(buf, (gchar *)*c->ptr);
283 else if(c->type == TYPE_INT) {
284 char *b = itos((int)*c->ptr);
285 g_string_append(buf, b);
289 if(upto == ' ') s = vend;
294 mycmd = expand(ret, 1);
295 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
299 g_printerr("error on running command: %s\n", err->message);
302 else if (*cmd_stdout) {
303 g_string_append(buf, cmd_stdout);
310 mycmd = expand(ret, 1);
311 eval_js(uzbl.gui.web_view, mycmd, js_ret);
315 g_string_append(buf, js_ret->str);
316 g_string_free(js_ret, 1);
323 g_string_append_c(buf, *s);
328 return g_string_free(buf, FALSE);
335 snprintf(tmp, sizeof(tmp), "%i", val);
336 return g_strdup(tmp);
340 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
343 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
346 str_replace (const char* search, const char* replace, const char* string) {
350 buf = g_strsplit (string, search, -1);
351 ret = g_strjoinv (replace, buf);
352 g_strfreev(buf); // somebody said this segfaults
358 read_file_by_line (gchar *path) {
359 GIOChannel *chan = NULL;
360 gchar *readbuf = NULL;
362 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
365 chan = g_io_channel_new_file(path, "r", NULL);
368 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
369 const gchar* val = g_strdup (readbuf);
370 g_array_append_val (lines, val);
375 g_io_channel_unref (chan);
377 fprintf(stderr, "File '%s' not be read.\n", path);
384 gchar* parseenv (char* string) {
385 extern char** environ;
386 gchar* tmpstr = NULL;
390 while (environ[i] != NULL) {
391 gchar** env = g_strsplit (environ[i], "=", 2);
392 gchar* envname = g_strconcat ("$", env[0], NULL);
394 if (g_strrstr (string, envname) != NULL) {
395 tmpstr = g_strdup(string);
397 string = str_replace(envname, env[1], tmpstr);
402 g_strfreev (env); // somebody said this breaks uzbl
410 setup_signal(int signr, sigfunc *shandler) {
411 struct sigaction nh, oh;
413 nh.sa_handler = shandler;
414 sigemptyset(&nh.sa_mask);
417 if(sigaction(signr, &nh, &oh) < 0)
425 if (uzbl.behave.fifo_dir)
426 unlink (uzbl.comm.fifo_path);
427 if (uzbl.behave.socket_dir)
428 unlink (uzbl.comm.socket_path);
430 g_free(uzbl.state.executable_path);
431 g_string_free(uzbl.state.keycmd, TRUE);
432 g_hash_table_destroy(uzbl.bindings);
433 g_hash_table_destroy(uzbl.behave.commands);
436 /* used for html_mode_timeout
437 * be sure to extend this function to use
438 * more timers if needed in other places
441 set_timeout(int seconds) {
443 memset(&t, 0, sizeof t);
445 t.it_value.tv_sec = seconds;
446 t.it_value.tv_usec = 0;
447 setitimer(ITIMER_REAL, &t, NULL);
450 /* --- SIGNAL HANDLER --- */
453 catch_sigterm(int s) {
459 catch_sigint(int s) {
469 set_var_value("mode", "0");
474 /* --- CALLBACKS --- */
477 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
480 (void) navigation_action;
481 (void) policy_decision;
483 const gchar* uri = webkit_network_request_get_uri (request);
484 if (uzbl.state.verbose)
485 printf("New window requested -> %s \n", uri);
486 new_window_load_uri(uri);
491 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
496 /* If we can display it, let's display it... */
497 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
498 webkit_web_policy_decision_use (policy_decision);
502 /* ...everything we can't displayed is downloaded */
503 webkit_web_policy_decision_download (policy_decision);
508 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
512 if (uzbl.state.selected_url != NULL) {
513 if (uzbl.state.verbose)
514 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
515 new_window_load_uri(uzbl.state.selected_url);
517 if (uzbl.state.verbose)
518 printf("New web view -> %s\n","Nothing to open, exiting");
524 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
527 if (uzbl.behave.download_handler) {
528 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
529 if (uzbl.state.verbose)
530 printf("Download -> %s\n",uri);
531 /* if urls not escaped, we may have to escape and quote uri before this call */
532 run_handler(uzbl.behave.download_handler, uri);
537 /* scroll a bar in a given direction */
539 scroll (GtkAdjustment* bar, GArray *argv) {
543 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
544 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
545 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
549 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
550 (void) page; (void) argv; (void) result;
551 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
555 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
556 (void) page; (void) argv; (void) result;
557 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
558 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
562 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
563 (void) page; (void) result;
564 scroll(uzbl.gui.bar_v, argv);
568 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
569 (void) page; (void) result;
570 scroll(uzbl.gui.bar_h, argv);
575 if (!uzbl.behave.show_status) {
576 gtk_widget_hide(uzbl.gui.mainbar);
578 gtk_widget_show(uzbl.gui.mainbar);
584 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
589 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
593 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
598 if (uzbl.behave.show_status) {
599 gtk_widget_hide(uzbl.gui.mainbar);
601 gtk_widget_show(uzbl.gui.mainbar);
603 uzbl.behave.show_status = !uzbl.behave.show_status;
608 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
612 //Set selected_url state variable
613 g_free(uzbl.state.selected_url);
614 uzbl.state.selected_url = NULL;
616 uzbl.state.selected_url = g_strdup(link);
622 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
626 if (uzbl.gui.main_title)
627 g_free (uzbl.gui.main_title);
628 uzbl.gui.main_title = g_strdup (title);
633 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
636 uzbl.gui.sbar.load_progress = progress;
641 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
645 if (uzbl.behave.load_finish_handler)
646 run_handler(uzbl.behave.load_finish_handler, "");
650 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
654 uzbl.gui.sbar.load_progress = 0;
655 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
656 if (uzbl.behave.load_start_handler)
657 run_handler(uzbl.behave.load_start_handler, "");
661 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
664 g_free (uzbl.state.uri);
665 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
666 uzbl.state.uri = g_string_free (newuri, FALSE);
667 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
668 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
671 if (uzbl.behave.load_commit_handler)
672 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
676 destroy_cb (GtkWidget* widget, gpointer data) {
684 if (uzbl.behave.history_handler) {
686 struct tm * timeinfo;
689 timeinfo = localtime ( &rawtime );
690 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
691 run_handler(uzbl.behave.history_handler, date);
696 /* VIEW funcs (little webkit wrappers) */
697 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
699 VIEWFUNC(reload_bypass_cache)
700 VIEWFUNC(stop_loading)
707 /* -- command to callback/function map for things we cannot attach to any signals */
708 static struct {char *key; CommandInfo value;} cmdlist[] =
709 { /* key function no_split */
710 { "back", {view_go_back, 0} },
711 { "forward", {view_go_forward, 0} },
712 { "scroll_vert", {scroll_vert, 0} },
713 { "scroll_horz", {scroll_horz, 0} },
714 { "scroll_begin", {scroll_begin, 0} },
715 { "scroll_end", {scroll_end, 0} },
716 { "reload", {view_reload, 0}, },
717 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
718 { "stop", {view_stop_loading, 0}, },
719 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
720 { "zoom_out", {view_zoom_out, 0}, },
721 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
722 { "uri", {load_uri, TRUE} },
723 { "js", {run_js, TRUE} },
724 { "script", {run_external_js, 0} },
725 { "toggle_status", {toggle_status_cb, 0} },
726 { "spawn", {spawn, 0} },
727 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
728 { "sh", {spawn_sh, 0} },
729 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
730 { "exit", {close_uzbl, 0} },
731 { "search", {search_forward_text, TRUE} },
732 { "search_reverse", {search_reverse_text, TRUE} },
733 { "dehilight", {dehilight, 0} },
734 { "toggle_insert_mode", {toggle_insert_mode, 0} },
735 { "set", {set_var, TRUE} },
736 //{ "get", {get_var, TRUE} },
737 { "bind", {act_bind, TRUE} },
738 { "dump_config", {act_dump_config, 0} },
739 { "keycmd", {keycmd, TRUE} },
740 { "keycmd_nl", {keycmd_nl, TRUE} },
741 { "keycmd_bs", {keycmd_bs, 0} },
742 { "chain", {chain, 0} },
743 { "print", {print, TRUE} }
750 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
752 for (i = 0; i < LENGTH(cmdlist); i++)
753 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
756 /* -- CORE FUNCTIONS -- */
759 free_action(gpointer act) {
760 Action *action = (Action*)act;
761 g_free(action->name);
763 g_free(action->param);
768 new_action(const gchar *name, const gchar *param) {
769 Action *action = g_new(Action, 1);
771 action->name = g_strdup(name);
773 action->param = g_strdup(param);
775 action->param = NULL;
781 file_exists (const char * filename) {
782 return (access(filename, F_OK) == 0);
786 set_var(WebKitWebView *page, GArray *argv, GString *result) {
787 (void) page; (void) result;
788 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
789 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
790 set_var_value(g_strstrip(split[0]), value);
796 print(WebKitWebView *page, GArray *argv, GString *result) {
797 (void) page; (void) result;
800 buf = expand(argv_idx(argv, 0), 0);
801 g_string_assign(result, buf);
806 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
807 (void) page; (void) result;
808 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
809 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
810 add_binding(g_strstrip(split[0]), value);
822 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
823 (void) page; (void) result;
825 if (argv_idx(argv, 0)) {
826 if (strcmp (argv_idx(argv, 0), "0") == 0) {
827 uzbl.behave.insert_mode = FALSE;
829 uzbl.behave.insert_mode = TRUE;
832 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
839 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
842 if (argv_idx(argv, 0)) {
843 GString* newuri = g_string_new (argv_idx(argv, 0));
844 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
845 run_js(web_view, argv, NULL);
848 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
849 g_string_prepend (newuri, "http://");
850 /* if we do handle cookies, ask our handler for them */
851 webkit_web_view_load_uri (web_view, newuri->str);
852 g_string_free (newuri, TRUE);
860 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
861 size_t argumentCount, const JSValueRef arguments[],
862 JSValueRef* exception) {
867 JSStringRef js_result_string;
868 GString *result = g_string_new("");
870 if (argumentCount >= 1) {
871 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
872 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
873 char ctl_line[arg_size];
874 JSStringGetUTF8CString(arg, ctl_line, arg_size);
876 parse_cmd_line(ctl_line, result);
878 JSStringRelease(arg);
880 js_result_string = JSStringCreateWithUTF8CString(result->str);
882 g_string_free(result, TRUE);
884 return JSValueMakeString(ctx, js_result_string);
887 static JSStaticFunction js_static_functions[] = {
888 {"run", js_run_command, kJSPropertyAttributeNone},
893 /* This function creates the class and its definition, only once */
894 if (!uzbl.js.initialized) {
895 /* it would be pretty cool to make this dynamic */
896 uzbl.js.classdef = kJSClassDefinitionEmpty;
897 uzbl.js.classdef.staticFunctions = js_static_functions;
899 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
905 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
906 WebKitWebFrame *frame;
907 JSGlobalContextRef context;
908 JSObjectRef globalobject;
909 JSStringRef var_name;
911 JSStringRef js_script;
912 JSValueRef js_result;
913 JSStringRef js_result_string;
914 size_t js_result_size;
918 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
919 context = webkit_web_frame_get_global_context(frame);
920 globalobject = JSContextGetGlobalObject(context);
922 /* uzbl javascript namespace */
923 var_name = JSStringCreateWithUTF8CString("Uzbl");
924 JSObjectSetProperty(context, globalobject, var_name,
925 JSObjectMake(context, uzbl.js.classref, NULL),
926 kJSClassAttributeNone, NULL);
928 /* evaluate the script and get return value*/
929 js_script = JSStringCreateWithUTF8CString(script);
930 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
931 if (js_result && !JSValueIsUndefined(context, js_result)) {
932 js_result_string = JSValueToStringCopy(context, js_result, NULL);
933 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
935 if (js_result_size) {
936 char js_result_utf8[js_result_size];
937 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
938 g_string_assign(result, js_result_utf8);
941 JSStringRelease(js_result_string);
945 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
947 JSStringRelease(var_name);
948 JSStringRelease(js_script);
952 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
954 if (argv_idx(argv, 0))
955 eval_js(web_view, argv_idx(argv, 0), result);
959 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
961 if (argv_idx(argv, 0)) {
962 GArray* lines = read_file_by_line (argv_idx (argv, 0));
967 while ((line = g_array_index(lines, gchar*, i))) {
969 js = g_strdup (line);
971 gchar* newjs = g_strconcat (js, line, NULL);
978 if (uzbl.state.verbose)
979 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
981 if (argv_idx (argv, 1)) {
982 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
986 eval_js (web_view, js, result);
988 g_array_free (lines, TRUE);
993 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
994 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
995 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
996 webkit_web_view_unmark_text_matches (page);
997 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
998 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1002 if (uzbl.state.searchtx) {
1003 if (uzbl.state.verbose)
1004 printf ("Searching: %s\n", uzbl.state.searchtx);
1005 webkit_web_view_set_highlight_text_matches (page, TRUE);
1006 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1011 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1013 search_text(page, argv, TRUE);
1017 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1019 search_text(page, argv, FALSE);
1023 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1024 (void) argv; (void) result;
1025 webkit_web_view_set_highlight_text_matches (page, FALSE);
1030 new_window_load_uri (const gchar * uri) {
1031 GString* to_execute = g_string_new ("");
1032 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1034 for (i = 0; entries[i].long_name != NULL; i++) {
1035 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1036 gchar** str = (gchar**)entries[i].arg_data;
1038 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1042 if (uzbl.state.verbose)
1043 printf("\n%s\n", to_execute->str);
1044 g_spawn_command_line_async (to_execute->str, NULL);
1045 g_string_free (to_execute, TRUE);
1049 chain (WebKitWebView *page, GArray *argv, GString *result) {
1050 (void) page; (void) result;
1052 gchar **parts = NULL;
1054 while ((a = argv_idx(argv, i++))) {
1055 parts = g_strsplit (a, " ", 2);
1056 parse_command(parts[0], parts[1], result);
1062 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1066 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1072 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1076 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1082 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1087 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1089 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1094 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1101 /* --Statusbar functions-- */
1103 build_progressbar_ascii(int percent) {
1104 int width=uzbl.gui.sbar.progress_w;
1107 GString *bar = g_string_new("");
1109 l = (double)percent*((double)width/100.);
1110 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1112 for(i=0; i<(int)l; i++)
1113 g_string_append(bar, uzbl.gui.sbar.progress_s);
1116 g_string_append(bar, uzbl.gui.sbar.progress_u);
1118 return g_string_free(bar, FALSE);
1123 const GScannerConfig scan_config = {
1126 ) /* cset_skip_characters */,
1131 ) /* cset_identifier_first */,
1138 ) /* cset_identifier_nth */,
1139 ( "" ) /* cpair_comment_single */,
1141 TRUE /* case_sensitive */,
1143 FALSE /* skip_comment_multi */,
1144 FALSE /* skip_comment_single */,
1145 FALSE /* scan_comment_multi */,
1146 TRUE /* scan_identifier */,
1147 TRUE /* scan_identifier_1char */,
1148 FALSE /* scan_identifier_NULL */,
1149 TRUE /* scan_symbols */,
1150 FALSE /* scan_binary */,
1151 FALSE /* scan_octal */,
1152 FALSE /* scan_float */,
1153 FALSE /* scan_hex */,
1154 FALSE /* scan_hex_dollar */,
1155 FALSE /* scan_string_sq */,
1156 FALSE /* scan_string_dq */,
1157 TRUE /* numbers_2_int */,
1158 FALSE /* int_2_float */,
1159 FALSE /* identifier_2_string */,
1160 FALSE /* char_2_token */,
1161 FALSE /* symbol_2_token */,
1162 TRUE /* scope_0_fallback */,
1167 uzbl.scan = g_scanner_new(&scan_config);
1168 while(symp->symbol_name) {
1169 g_scanner_scope_add_symbol(uzbl.scan, 0,
1171 GINT_TO_POINTER(symp->symbol_token));
1177 expand_template(const char *template, gboolean escape_markup) {
1178 if(!template) return NULL;
1180 GTokenType token = G_TOKEN_NONE;
1181 GString *ret = g_string_new("");
1185 g_scanner_input_text(uzbl.scan, template, strlen(template));
1186 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1187 token = g_scanner_get_next_token(uzbl.scan);
1189 if(token == G_TOKEN_SYMBOL) {
1190 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1194 buf = uzbl.state.uri?
1195 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1196 g_string_append(ret, buf);
1200 g_string_append(ret, uzbl.state.uri?
1201 uzbl.state.uri:g_strdup(""));
1204 buf = itos(uzbl.gui.sbar.load_progress);
1205 g_string_append(ret, buf);
1208 case SYM_LOADPRGSBAR:
1209 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1210 g_string_append(ret, buf);
1215 buf = uzbl.gui.main_title?
1216 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1217 g_string_append(ret, buf);
1221 g_string_append(ret, uzbl.gui.main_title?
1222 uzbl.gui.main_title:g_strdup(""));
1224 case SYM_SELECTED_URI:
1226 buf = uzbl.state.selected_url?
1227 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1228 g_string_append(ret, buf);
1232 g_string_append(ret, uzbl.state.selected_url?
1233 uzbl.state.selected_url:g_strdup(""));
1236 buf = itos(uzbl.xwin);
1237 g_string_append(ret,
1238 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1243 buf = uzbl.state.keycmd->str?
1244 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1245 g_string_append(ret, buf);
1249 g_string_append(ret, uzbl.state.keycmd->str?
1250 uzbl.state.keycmd->str:g_strdup(""));
1253 g_string_append(ret,
1254 uzbl.behave.insert_mode?
1255 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1258 g_string_append(ret,
1259 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1261 /* useragent syms */
1263 buf = itos(WEBKIT_MAJOR_VERSION);
1264 g_string_append(ret, buf);
1268 buf = itos(WEBKIT_MINOR_VERSION);
1269 g_string_append(ret, buf);
1273 buf = itos(WEBKIT_MICRO_VERSION);
1274 g_string_append(ret, buf);
1278 g_string_append(ret, uzbl.state.unameinfo.sysname);
1281 g_string_append(ret, uzbl.state.unameinfo.nodename);
1284 g_string_append(ret, uzbl.state.unameinfo.release);
1287 g_string_append(ret, uzbl.state.unameinfo.version);
1290 g_string_append(ret, uzbl.state.unameinfo.machine);
1293 g_string_append(ret, ARCH);
1296 case SYM_DOMAINNAME:
1297 g_string_append(ret, uzbl.state.unameinfo.domainname);
1301 g_string_append(ret, COMMIT);
1307 else if(token == G_TOKEN_INT) {
1308 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1309 g_string_append(ret, buf);
1312 else if(token == G_TOKEN_IDENTIFIER) {
1313 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1315 else if(token == G_TOKEN_CHAR) {
1316 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1320 return g_string_free(ret, FALSE);
1322 /* --End Statusbar functions-- */
1325 sharg_append(GArray *a, const gchar *str) {
1326 const gchar *s = (str ? str : "");
1327 g_array_append_val(a, s);
1330 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1332 run_command (const gchar *command, const guint npre, const gchar **args,
1333 const gboolean sync, char **output_stdout) {
1334 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1337 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1338 gchar *pid = itos(getpid());
1339 gchar *xwin = itos(uzbl.xwin);
1341 sharg_append(a, command);
1342 for (i = 0; i < npre; i++) /* add n args before the default vars */
1343 sharg_append(a, args[i]);
1344 sharg_append(a, uzbl.state.config_file);
1345 sharg_append(a, pid);
1346 sharg_append(a, xwin);
1347 sharg_append(a, uzbl.comm.fifo_path);
1348 sharg_append(a, uzbl.comm.socket_path);
1349 sharg_append(a, uzbl.state.uri);
1350 sharg_append(a, uzbl.gui.main_title);
1352 for (i = npre; i < g_strv_length((gchar**)args); i++)
1353 sharg_append(a, args[i]);
1357 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1359 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1360 NULL, NULL, output_stdout, NULL, NULL, &err);
1361 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1362 NULL, NULL, NULL, &err);
1364 if (uzbl.state.verbose) {
1365 GString *s = g_string_new("spawned:");
1366 for (i = 0; i < (a->len); i++) {
1367 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1368 g_string_append_printf(s, " %s", qarg);
1371 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1372 printf("%s\n", s->str);
1373 g_string_free(s, TRUE);
1375 printf("Stdout: %s\n", *output_stdout);
1379 g_printerr("error on run_command: %s\n", err->message);
1384 g_array_free (a, TRUE);
1389 split_quoted(const gchar* src, const gboolean unquote) {
1390 /* split on unquoted space, return array of strings;
1391 remove a layer of quotes and backslashes if unquote */
1392 if (!src) return NULL;
1394 gboolean dq = FALSE;
1395 gboolean sq = FALSE;
1396 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1397 GString *s = g_string_new ("");
1401 for (p = src; *p != '\0'; p++) {
1402 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1403 else if (*p == '\\') { g_string_append_c(s, *p++);
1404 g_string_append_c(s, *p); }
1405 else if ((*p == '"') && unquote && !sq) dq = !dq;
1406 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1408 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1409 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1411 else if ((*p == ' ') && !dq && !sq) {
1412 dup = g_strdup(s->str);
1413 g_array_append_val(a, dup);
1414 g_string_truncate(s, 0);
1415 } else g_string_append_c(s, *p);
1417 dup = g_strdup(s->str);
1418 g_array_append_val(a, dup);
1419 ret = (gchar**)a->data;
1420 g_array_free (a, FALSE);
1421 g_string_free (s, TRUE);
1426 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1427 (void)web_view; (void)result;
1428 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1429 if (argv_idx(argv, 0))
1430 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1434 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1435 (void)web_view; (void)result;
1437 if (argv_idx(argv, 0))
1438 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1439 TRUE, &uzbl.comm.sync_stdout);
1443 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1444 (void)web_view; (void)result;
1445 if (!uzbl.behave.shell_cmd) {
1446 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1451 gchar *spacer = g_strdup("");
1452 g_array_insert_val(argv, 1, spacer);
1453 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1455 for (i = 1; i < g_strv_length(cmd); i++)
1456 g_array_prepend_val(argv, cmd[i]);
1458 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1464 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1465 (void)web_view; (void)result;
1466 if (!uzbl.behave.shell_cmd) {
1467 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1472 gchar *spacer = g_strdup("");
1473 g_array_insert_val(argv, 1, spacer);
1474 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1476 for (i = 1; i < g_strv_length(cmd); i++)
1477 g_array_prepend_val(argv, cmd[i]);
1479 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1480 TRUE, &uzbl.comm.sync_stdout);
1486 parse_command(const char *cmd, const char *param, GString *result) {
1489 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1491 gchar **par = split_quoted(param, TRUE);
1492 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1494 if (c->no_split) { /* don't split */
1495 sharg_append(a, param);
1497 for (i = 0; i < g_strv_length(par); i++)
1498 sharg_append(a, par[i]);
1501 if (result == NULL) {
1502 GString *result_print = g_string_new("");
1504 c->function(uzbl.gui.web_view, a, result_print);
1505 if (result_print->len)
1506 printf("%*s\n", result_print->len, result_print->str);
1508 g_string_free(result_print, TRUE);
1510 c->function(uzbl.gui.web_view, a, result);
1513 g_array_free (a, TRUE);
1516 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1523 if(*uzbl.net.proxy_url == ' '
1524 || uzbl.net.proxy_url == NULL) {
1525 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1526 (GType) SOUP_SESSION_PROXY_URI);
1529 suri = soup_uri_new(uzbl.net.proxy_url);
1530 g_object_set(G_OBJECT(uzbl.net.soup_session),
1531 SOUP_SESSION_PROXY_URI,
1533 soup_uri_free(suri);
1540 if(file_exists(uzbl.gui.icon)) {
1541 if (uzbl.gui.main_window)
1542 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1544 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1546 g_free (uzbl.gui.icon);
1551 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1552 g_array_append_val (a, uzbl.state.uri);
1553 load_uri(uzbl.gui.web_view, a, NULL);
1554 g_array_free (a, TRUE);
1558 cmd_always_insert_mode() {
1559 uzbl.behave.insert_mode =
1560 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1566 g_object_set(G_OBJECT(uzbl.net.soup_session),
1567 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1571 cmd_max_conns_host() {
1572 g_object_set(G_OBJECT(uzbl.net.soup_session),
1573 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1578 soup_session_remove_feature
1579 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1580 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1581 /*g_free(uzbl.net.soup_logger);*/
1583 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1584 soup_session_add_feature(uzbl.net.soup_session,
1585 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1588 static WebKitWebSettings*
1590 return webkit_web_view_get_settings(uzbl.gui.web_view);
1595 WebKitWebSettings *ws = view_settings();
1596 if (uzbl.behave.font_size > 0) {
1597 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1600 if (uzbl.behave.monospace_size > 0) {
1601 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1602 uzbl.behave.monospace_size, NULL);
1604 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1605 uzbl.behave.font_size, NULL);
1611 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1615 cmd_disable_plugins() {
1616 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1617 !uzbl.behave.disable_plugins, NULL);
1621 cmd_disable_scripts() {
1622 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1623 !uzbl.behave.disable_scripts, NULL);
1627 cmd_minimum_font_size() {
1628 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1629 uzbl.behave.minimum_font_size, NULL);
1632 cmd_autoload_img() {
1633 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1634 uzbl.behave.autoload_img, NULL);
1639 cmd_autoshrink_img() {
1640 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1641 uzbl.behave.autoshrink_img, NULL);
1646 cmd_enable_spellcheck() {
1647 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1648 uzbl.behave.enable_spellcheck, NULL);
1652 cmd_enable_private() {
1653 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1654 uzbl.behave.enable_private, NULL);
1659 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1660 uzbl.behave.print_bg, NULL);
1665 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1666 uzbl.behave.style_uri, NULL);
1670 cmd_resizable_txt() {
1671 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1672 uzbl.behave.resizable_txt, NULL);
1676 cmd_default_encoding() {
1677 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1678 uzbl.behave.default_encoding, NULL);
1682 cmd_enforce_96dpi() {
1683 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1684 uzbl.behave.enforce_96dpi, NULL);
1688 cmd_caret_browsing() {
1689 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1690 uzbl.behave.caret_browsing, NULL);
1694 cmd_cookie_handler() {
1695 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1696 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1697 if ((g_strcmp0(split[0], "sh") == 0) ||
1698 (g_strcmp0(split[0], "spawn") == 0)) {
1699 g_free (uzbl.behave.cookie_handler);
1700 uzbl.behave.cookie_handler =
1701 g_strdup_printf("sync_%s %s", split[0], split[1]);
1708 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1713 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1718 if(uzbl.behave.inject_html) {
1719 webkit_web_view_load_html_string (uzbl.gui.web_view,
1720 uzbl.behave.inject_html, NULL);
1729 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1730 uzbl.behave.modmask = 0;
1732 if(uzbl.behave.modkey)
1733 g_free(uzbl.behave.modkey);
1734 uzbl.behave.modkey = buf;
1736 for (i = 0; modkeys[i].key != NULL; i++) {
1737 if (g_strrstr(buf, modkeys[i].key))
1738 uzbl.behave.modmask |= modkeys[i].mask;
1744 if (*uzbl.net.useragent == ' ') {
1745 g_free (uzbl.net.useragent);
1746 uzbl.net.useragent = NULL;
1748 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1750 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1751 g_free(uzbl.net.useragent);
1752 uzbl.net.useragent = ua;
1758 gtk_widget_ref(uzbl.gui.scrolled_win);
1759 gtk_widget_ref(uzbl.gui.mainbar);
1760 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1761 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1763 if(uzbl.behave.status_top) {
1764 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1765 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1768 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1769 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1771 gtk_widget_unref(uzbl.gui.scrolled_win);
1772 gtk_widget_unref(uzbl.gui.mainbar);
1773 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1778 set_var_value(gchar *name, gchar *val) {
1779 uzbl_cmdprop *c = NULL;
1783 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1784 /* check for the variable type */
1785 if (c->type == TYPE_STR) {
1786 buf = expand(val, 0);
1789 } else if(c->type == TYPE_INT) {
1790 int *ip = (int *)c->ptr;
1791 buf = expand(val, 0);
1792 *ip = (int)strtoul(buf, &endp, 10);
1794 } else if (c->type == TYPE_FLOAT) {
1795 float *fp = (float *)c->ptr;
1796 buf = expand(val, 0);
1797 *fp = strtod(buf, &endp);
1801 /* invoke a command specific function */
1802 if(c->func) c->func();
1809 Behaviour *b = &uzbl.behave;
1811 if(b->html_buffer->str) {
1812 webkit_web_view_load_html_string (uzbl.gui.web_view,
1813 b->html_buffer->str, b->base_url);
1814 g_string_free(b->html_buffer, TRUE);
1815 b->html_buffer = g_string_new("");
1819 enum {M_CMD, M_HTML};
1821 parse_cmd_line(const char *ctl_line, GString *result) {
1822 Behaviour *b = &uzbl.behave;
1825 if(b->mode == M_HTML) {
1826 len = strlen(b->html_endmarker);
1827 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1828 if(len == strlen(ctl_line)-1 &&
1829 !strncmp(b->html_endmarker, ctl_line, len)) {
1831 set_var_value("mode", "0");
1836 set_timeout(b->html_timeout);
1837 g_string_append(b->html_buffer, ctl_line);
1840 else if((ctl_line[0] == '#') /* Comments */
1841 || (ctl_line[0] == ' ')
1842 || (ctl_line[0] == '\n'))
1843 ; /* ignore these lines */
1844 else { /* parse a command */
1846 gchar **tokens = NULL;
1847 len = strlen(ctl_line);
1849 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1850 ctlstrip = g_strndup(ctl_line, len - 1);
1851 else ctlstrip = g_strdup(ctl_line);
1853 tokens = g_strsplit(ctlstrip, " ", 2);
1854 parse_command(tokens[0], tokens[1], result);
1861 build_stream_name(int type, const gchar* dir) {
1862 char *xwin_str = NULL;
1863 State *s = &uzbl.state;
1866 xwin_str = itos((int)uzbl.xwin);
1868 str = g_strdup_printf
1869 ("%s/uzbl_fifo_%s", dir,
1870 s->instance_name ? s->instance_name : xwin_str);
1871 } else if (type == SOCKET) {
1872 str = g_strdup_printf
1873 ("%s/uzbl_socket_%s", dir,
1874 s->instance_name ? s->instance_name : xwin_str );
1881 control_fifo(GIOChannel *gio, GIOCondition condition) {
1882 if (uzbl.state.verbose)
1883 printf("triggered\n");
1888 if (condition & G_IO_HUP)
1889 g_error ("Fifo: Read end of pipe died!\n");
1892 g_error ("Fifo: GIOChannel broke\n");
1894 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1895 if (ret == G_IO_STATUS_ERROR) {
1896 g_error ("Fifo: Error reading: %s\n", err->message);
1900 parse_cmd_line(ctl_line, NULL);
1907 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1908 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1909 if (unlink(uzbl.comm.fifo_path) == -1)
1910 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1911 g_free(uzbl.comm.fifo_path);
1912 uzbl.comm.fifo_path = NULL;
1915 if (*dir == ' ') { /* space unsets the variable */
1920 GIOChannel *chan = NULL;
1921 GError *error = NULL;
1922 gchar *path = build_stream_name(FIFO, dir);
1924 if (!file_exists(path)) {
1925 if (mkfifo (path, 0666) == 0) {
1926 // 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.
1927 chan = g_io_channel_new_file(path, "r+", &error);
1929 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1930 if (uzbl.state.verbose)
1931 printf ("init_fifo: created successfully as %s\n", path);
1932 uzbl.comm.fifo_path = path;
1934 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1935 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1936 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1937 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1939 /* if we got this far, there was an error; cleanup */
1940 if (error) g_error_free (error);
1947 control_stdin(GIOChannel *gio, GIOCondition condition) {
1949 gchar *ctl_line = NULL;
1952 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1953 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1956 parse_cmd_line(ctl_line, NULL);
1964 GIOChannel *chan = NULL;
1965 GError *error = NULL;
1967 chan = g_io_channel_unix_new(fileno(stdin));
1969 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1970 g_error ("Stdin: could not add watch\n");
1972 if (uzbl.state.verbose)
1973 printf ("Stdin: watch added successfully\n");
1976 g_error ("Stdin: Error while opening: %s\n", error->message);
1978 if (error) g_error_free (error);
1982 control_socket(GIOChannel *chan) {
1983 struct sockaddr_un remote;
1984 unsigned int t = sizeof(remote);
1986 GIOChannel *clientchan;
1988 clientsock = accept (g_io_channel_unix_get_fd(chan),
1989 (struct sockaddr *) &remote, &t);
1991 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1992 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1993 (GIOFunc) control_client_socket, clientchan);
2000 control_client_socket(GIOChannel *clientchan) {
2002 GString *result = g_string_new("");
2003 GError *error = NULL;
2007 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2008 if (ret == G_IO_STATUS_ERROR) {
2009 g_warning ("Error reading: %s\n", error->message);
2010 g_io_channel_shutdown(clientchan, TRUE, &error);
2012 } else if (ret == G_IO_STATUS_EOF) {
2013 /* shutdown and remove channel watch from main loop */
2014 g_io_channel_shutdown(clientchan, TRUE, &error);
2019 parse_cmd_line (ctl_line, result);
2020 g_string_append_c(result, '\n');
2021 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2023 if (ret == G_IO_STATUS_ERROR) {
2024 g_warning ("Error writing: %s", error->message);
2026 g_io_channel_flush(clientchan, &error);
2029 if (error) g_error_free (error);
2030 g_string_free(result, TRUE);
2036 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2037 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2038 if (unlink(uzbl.comm.socket_path) == -1)
2039 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2040 g_free(uzbl.comm.socket_path);
2041 uzbl.comm.socket_path = NULL;
2049 GIOChannel *chan = NULL;
2051 struct sockaddr_un local;
2052 gchar *path = build_stream_name(SOCKET, dir);
2054 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2056 local.sun_family = AF_UNIX;
2057 strcpy (local.sun_path, path);
2058 unlink (local.sun_path);
2060 len = strlen (local.sun_path) + sizeof (local.sun_family);
2061 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2062 if (uzbl.state.verbose)
2063 printf ("init_socket: opened in %s\n", path);
2066 if( (chan = g_io_channel_unix_new(sock)) ) {
2067 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2068 uzbl.comm.socket_path = path;
2071 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2073 /* if we got this far, there was an error; cleanup */
2080 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2081 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2083 // this function may be called very early when the templates are not set (yet), hence the checks
2085 update_title (void) {
2086 Behaviour *b = &uzbl.behave;
2089 if (b->show_status) {
2090 if (b->title_format_short) {
2091 parsed = expand_template(b->title_format_short, FALSE);
2092 if (uzbl.gui.main_window)
2093 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2096 if (b->status_format) {
2097 parsed = expand_template(b->status_format, TRUE);
2098 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2101 if (b->status_background) {
2103 gdk_color_parse (b->status_background, &color);
2104 //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)
2105 if (uzbl.gui.main_window)
2106 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2109 if (b->title_format_long) {
2110 parsed = expand_template(b->title_format_long, FALSE);
2111 if (uzbl.gui.main_window)
2112 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2119 key_press_cb (GtkWidget* window, GdkEventKey* event)
2121 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2125 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2126 || 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)
2129 /* turn off insert mode (if always_insert_mode is not used) */
2130 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2131 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2136 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2139 if (event->keyval == GDK_Escape) {
2140 g_string_truncate(uzbl.state.keycmd, 0);
2142 dehilight(uzbl.gui.web_view, NULL, NULL);
2146 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2147 if (event->keyval == GDK_Insert) {
2149 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2150 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2152 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2155 g_string_append (uzbl.state.keycmd, str);
2162 if (event->keyval == GDK_BackSpace)
2163 keycmd_bs(NULL, NULL, NULL);
2165 gboolean key_ret = FALSE;
2166 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2168 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2170 run_keycmd(key_ret);
2172 if (key_ret) return (!uzbl.behave.insert_mode);
2177 run_keycmd(const gboolean key_ret) {
2178 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2180 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2181 g_string_truncate(uzbl.state.keycmd, 0);
2182 parse_command(act->name, act->param, NULL);
2186 /* try if it's an incremental keycmd or one that takes args, and run it */
2187 GString* short_keys = g_string_new ("");
2188 GString* short_keys_inc = g_string_new ("");
2190 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2191 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2192 g_string_assign(short_keys_inc, short_keys->str);
2193 g_string_append_c(short_keys, '_');
2194 g_string_append_c(short_keys_inc, '*');
2196 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2197 /* run normal cmds only if return was pressed */
2198 exec_paramcmd(act, i);
2199 g_string_truncate(uzbl.state.keycmd, 0);
2201 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2202 if (key_ret) /* just quit the incremental command on return */
2203 g_string_truncate(uzbl.state.keycmd, 0);
2204 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2208 g_string_truncate(short_keys, short_keys->len - 1);
2210 g_string_free (short_keys, TRUE);
2211 g_string_free (short_keys_inc, TRUE);
2215 exec_paramcmd(const Action *act, const guint i) {
2216 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2217 GString *actionname = g_string_new ("");
2218 GString *actionparam = g_string_new ("");
2219 g_string_erase (parampart, 0, i+1);
2221 g_string_printf (actionname, act->name, parampart->str);
2223 g_string_printf (actionparam, act->param, parampart->str);
2224 parse_command(actionname->str, actionparam->str, NULL);
2225 g_string_free(actionname, TRUE);
2226 g_string_free(actionparam, TRUE);
2227 g_string_free(parampart, TRUE);
2235 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2236 //main_window_ref = g_object_ref(scrolled_window);
2237 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
2239 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2240 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2242 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2243 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2244 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2245 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2246 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2247 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2248 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2249 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2250 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2251 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2252 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2254 return scrolled_window;
2261 g->mainbar = gtk_hbox_new (FALSE, 0);
2263 /* keep a reference to the bar so we can re-pack it at runtime*/
2264 //sbar_ref = g_object_ref(g->mainbar);
2266 g->mainbar_label = gtk_label_new ("");
2267 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2268 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2269 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2270 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2271 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2272 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2277 GtkWidget* create_window () {
2278 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2279 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2280 gtk_widget_set_name (window, "Uzbl browser");
2281 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2282 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2288 GtkPlug* create_plug () {
2289 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2290 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2291 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2298 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2300 If actname is one that calls an external command, this function will inject
2301 newargs in front of the user-provided args in that command line. They will
2302 come become after the body of the script (in sh) or after the name of
2303 the command to execute (in spawn).
2304 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2305 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2307 The return value consist of two strings: the action (sh, ...) and its args.
2309 If act is not one that calls an external command, then the given action merely
2312 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2313 gchar *actdup = g_strdup(actname);
2314 g_array_append_val(rets, actdup);
2316 if ((g_strcmp0(actname, "spawn") == 0) ||
2317 (g_strcmp0(actname, "sh") == 0) ||
2318 (g_strcmp0(actname, "sync_spawn") == 0) ||
2319 (g_strcmp0(actname, "sync_sh") == 0)) {
2321 GString *a = g_string_new("");
2322 gchar **spawnparts = split_quoted(origargs, FALSE);
2323 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2324 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2326 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2327 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2329 g_array_append_val(rets, a->str);
2330 g_string_free(a, FALSE);
2331 g_strfreev(spawnparts);
2333 gchar *origdup = g_strdup(origargs);
2334 g_array_append_val(rets, origdup);
2336 return (gchar**)g_array_free(rets, FALSE);
2340 run_handler (const gchar *act, const gchar *args) {
2341 /* Consider this code a temporary hack to make the handlers usable.
2342 In practice, all this splicing, injection, and reconstruction is
2343 inefficient, annoying and hard to manage. Potential pitfalls arise
2344 when the handler specific args 1) are not quoted (the handler
2345 callbacks should take care of this) 2) are quoted but interfere
2346 with the users' own quotation. A more ideal solution is
2347 to refactor parse_command so that it doesn't just take a string
2348 and execute it; rather than that, we should have a function which
2349 returns the argument vector parsed from the string. This vector
2350 could be modified (e.g. insert additional args into it) before
2351 passing it to the next function that actually executes it. Though
2352 it still isn't perfect for chain actions.. will reconsider & re-
2353 factor when I have the time. -duc */
2355 char **parts = g_strsplit(act, " ", 2);
2357 if (g_strcmp0(parts[0], "chain") == 0) {
2358 GString *newargs = g_string_new("");
2359 gchar **chainparts = split_quoted(parts[1], FALSE);
2361 /* for every argument in the chain, inject the handler args
2362 and make sure the new parts are wrapped in quotes */
2363 gchar **cp = chainparts;
2365 gchar *quotless = NULL;
2366 gchar **spliced_quotless = NULL; // sigh -_-;
2367 gchar **inpart = NULL;
2370 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2372 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2373 } else quotless = g_strdup(*cp);
2375 spliced_quotless = g_strsplit(quotless, " ", 2);
2376 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2377 g_strfreev(spliced_quotless);
2379 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2385 parse_command(parts[0], &(newargs->str[1]), NULL);
2386 g_string_free(newargs, TRUE);
2387 g_strfreev(chainparts);
2390 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2391 parse_command(inparts[0], inparts[1], NULL);
2399 add_binding (const gchar *key, const gchar *act) {
2400 char **parts = g_strsplit(act, " ", 2);
2407 if (uzbl.state.verbose)
2408 printf ("Binding %-10s : %s\n", key, act);
2409 action = new_action(parts[0], parts[1]);
2411 if (g_hash_table_remove (uzbl.bindings, key))
2412 g_warning ("Overwriting existing binding for \"%s\"", key);
2413 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2418 get_xdg_var (XDG_Var xdg) {
2419 const gchar* actual_value = getenv (xdg.environmental);
2420 const gchar* home = getenv ("HOME");
2421 gchar* return_value;
2423 if (! actual_value || strcmp (actual_value, "") == 0) {
2424 if (xdg.default_value) {
2425 return_value = str_replace ("~", home, xdg.default_value);
2427 return_value = NULL;
2430 return_value = str_replace("~", home, actual_value);
2433 return return_value;
2437 find_xdg_file (int xdg_type, char* filename) {
2438 /* xdg_type = 0 => config
2439 xdg_type = 1 => data
2440 xdg_type = 2 => cache*/
2442 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2443 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2446 gchar* temporary_string;
2450 if (! file_exists (temporary_file) && xdg_type != 2) {
2451 buf = get_xdg_var (XDG[3 + xdg_type]);
2452 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2455 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2456 g_free (temporary_file);
2457 temporary_file = g_strconcat (temporary_string, filename, NULL);
2461 //g_free (temporary_string); - segfaults.
2463 if (file_exists (temporary_file)) {
2464 return temporary_file;
2471 State *s = &uzbl.state;
2472 Network *n = &uzbl.net;
2474 for (i = 0; default_config[i].command != NULL; i++) {
2475 parse_cmd_line(default_config[i].command, NULL);
2478 if (g_strcmp0(s->config_file, "-") == 0) {
2479 s->config_file = NULL;
2483 if (!s->config_file) {
2484 s->config_file = find_xdg_file (0, "/uzbl/config");
2487 if (s->config_file) {
2488 GArray* lines = read_file_by_line (s->config_file);
2492 while ((line = g_array_index(lines, gchar*, i))) {
2493 parse_cmd_line (line, NULL);
2497 g_array_free (lines, TRUE);
2499 if (uzbl.state.verbose)
2500 printf ("No configuration file loaded.\n");
2503 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2506 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2509 if (!uzbl.behave.cookie_handler)
2512 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2513 GString *s = g_string_new ("");
2514 SoupURI * soup_uri = soup_message_get_uri(msg);
2515 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2516 run_handler(uzbl.behave.cookie_handler, s->str);
2518 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2519 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2520 if ( p != NULL ) *p = '\0';
2521 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2523 if (uzbl.comm.sync_stdout)
2524 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2526 g_string_free(s, TRUE);
2530 save_cookies (SoupMessage *msg, gpointer user_data){
2534 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2535 cookie = soup_cookie_to_set_cookie_header(ck->data);
2536 SoupURI * soup_uri = soup_message_get_uri(msg);
2537 GString *s = g_string_new ("");
2538 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2539 run_handler(uzbl.behave.cookie_handler, s->str);
2541 g_string_free(s, TRUE);
2546 /* --- WEBINSPECTOR --- */
2548 hide_window_cb(GtkWidget *widget, gpointer data) {
2551 gtk_widget_hide(widget);
2554 static WebKitWebView*
2555 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2558 (void) web_inspector;
2559 GtkWidget* scrolled_window;
2560 GtkWidget* new_web_view;
2563 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2564 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2565 G_CALLBACK(hide_window_cb), NULL);
2567 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2568 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2569 gtk_widget_show(g->inspector_window);
2571 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2572 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2573 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2574 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2575 gtk_widget_show(scrolled_window);
2577 new_web_view = webkit_web_view_new();
2578 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2580 return WEBKIT_WEB_VIEW(new_web_view);
2584 inspector_show_window_cb (WebKitWebInspector* inspector){
2586 gtk_widget_show(uzbl.gui.inspector_window);
2590 /* TODO: Add variables and code to make use of these functions */
2592 inspector_close_window_cb (WebKitWebInspector* inspector){
2598 inspector_attach_window_cb (WebKitWebInspector* inspector){
2604 inspector_detach_window_cb (WebKitWebInspector* inspector){
2610 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2616 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2622 set_up_inspector() {
2624 WebKitWebSettings *settings = view_settings();
2625 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2627 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2628 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2629 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2630 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2631 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2632 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2633 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2635 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2639 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2641 uzbl_cmdprop *c = v;
2646 if(c->type == TYPE_STR)
2647 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2648 else if(c->type == TYPE_INT)
2649 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2653 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2657 printf("bind %s = %s %s\n", (char *)k ,
2658 (char *)a->name, a->param?(char *)a->param:"");
2663 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2664 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2669 main (int argc, char* argv[]) {
2670 gtk_init (&argc, &argv);
2671 if (!g_thread_supported ())
2672 g_thread_init (NULL);
2673 uzbl.state.executable_path = g_strdup(argv[0]);
2674 uzbl.state.selected_url = NULL;
2675 uzbl.state.searchtx = NULL;
2677 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2678 g_option_context_add_main_entries (context, entries, NULL);
2679 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2680 g_option_context_parse (context, &argc, &argv, NULL);
2681 g_option_context_free(context);
2683 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2684 gboolean verbose_override = uzbl.state.verbose;
2686 /* initialize hash table */
2687 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2689 uzbl.net.soup_session = webkit_get_default_session();
2690 uzbl.state.keycmd = g_string_new("");
2692 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2693 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2694 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2695 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2696 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2697 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2700 if(uname(&uzbl.state.unameinfo) == -1)
2701 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2703 uzbl.gui.sbar.progress_s = g_strdup("=");
2704 uzbl.gui.sbar.progress_u = g_strdup("·");
2705 uzbl.gui.sbar.progress_w = 10;
2707 /* HTML mode defaults*/
2708 uzbl.behave.html_buffer = g_string_new("");
2709 uzbl.behave.html_endmarker = g_strdup(".");
2710 uzbl.behave.html_timeout = 60;
2711 uzbl.behave.base_url = g_strdup("http://invalid");
2713 /* default mode indicators */
2714 uzbl.behave.insert_indicator = g_strdup("I");
2715 uzbl.behave.cmd_indicator = g_strdup("C");
2719 make_var_to_name_hash();
2721 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2723 uzbl.gui.scrolled_win = create_browser();
2726 /* initial packing */
2727 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2728 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2730 if (uzbl.state.socket_id) {
2731 uzbl.gui.plug = create_plug ();
2732 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2733 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2735 uzbl.gui.main_window = create_window ();
2736 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2737 gtk_widget_show_all (uzbl.gui.main_window);
2738 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2741 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2743 if (uzbl.state.verbose) {
2744 printf("Uzbl start location: %s\n", argv[0]);
2745 if (uzbl.state.socket_id)
2746 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2748 printf("window_id %i\n",(int) uzbl.xwin);
2749 printf("pid %i\n", getpid ());
2750 printf("name: %s\n", uzbl.state.instance_name);
2753 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2754 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2755 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2756 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2757 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2761 if (!uzbl.behave.show_status)
2762 gtk_widget_hide(uzbl.gui.mainbar);
2769 if (verbose_override > uzbl.state.verbose)
2770 uzbl.state.verbose = verbose_override;
2773 set_var_value("uri", uri_override);
2774 g_free(uri_override);
2775 } else if (uzbl.state.uri)
2776 cmd_load_uri(uzbl.gui.web_view, NULL);
2781 return EXIT_SUCCESS;
2784 /* vi: set et ts=4: */