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;
212 * recurse == 1: don't expand '@(command)@'
213 * recurse == 2: don't expand '@<java script>@'
216 expand(char *s, guint recurse) {
220 char *end_simple_var = "^ยฐ!\"ยง$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]ยนยฒยณยผยฝ";
225 gchar *cmd_stdout = NULL;
227 GString *buf = g_string_new("");
228 GString *js_ret = g_string_new("");
233 g_string_append_c(buf, *++s);
238 etype = get_exp_type(s);
243 if( (vend = strpbrk(s, end_simple_var)) ||
244 (vend = strchr(s, '\0')) ) {
245 strncpy(ret, s, vend-s);
251 if( (vend = strchr(s, upto)) ||
252 (vend = strchr(s, '\0')) ) {
253 strncpy(ret, s, vend-s);
259 strcpy(str_end, ")@");
261 if( (vend = strstr(s, str_end)) ||
262 (vend = strchr(s, '\0')) ) {
263 strncpy(ret, s, vend-s);
269 strcpy(str_end, ">@");
271 if( (vend = strstr(s, str_end)) ||
272 (vend = strchr(s, '\0')) ) {
273 strncpy(ret, s, vend-s);
279 if(etype == EXP_SIMPLE_VAR ||
280 etype == EXP_BRACED_VAR) {
281 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
282 if(c->type == TYPE_STR)
283 g_string_append(buf, (gchar *)*c->ptr);
284 else if(c->type == TYPE_INT) {
285 char *b = itos((int)*c->ptr);
286 g_string_append(buf, b);
290 if(etype == EXP_SIMPLE_VAR)
295 else if(recurse != 1 &&
297 mycmd = expand(ret, 1);
298 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
302 g_printerr("error on running command: %s\n", err->message);
305 else if (*cmd_stdout) {
306 g_string_append(buf, cmd_stdout);
311 else if(recurse != 2 &&
313 mycmd = expand(ret, 2);
314 eval_js(uzbl.gui.web_view, mycmd, js_ret);
318 g_string_append(buf, js_ret->str);
319 g_string_free(js_ret, TRUE);
320 js_ret = g_string_new("");
327 g_string_append_c(buf, *s);
332 g_string_free(js_ret, TRUE);
333 return g_string_free(buf, FALSE);
340 snprintf(tmp, sizeof(tmp), "%i", val);
341 return g_strdup(tmp);
345 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
348 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
351 str_replace (const char* search, const char* replace, const char* string) {
355 buf = g_strsplit (string, search, -1);
356 ret = g_strjoinv (replace, buf);
357 g_strfreev(buf); // somebody said this segfaults
363 read_file_by_line (gchar *path) {
364 GIOChannel *chan = NULL;
365 gchar *readbuf = NULL;
367 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
370 chan = g_io_channel_new_file(path, "r", NULL);
373 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
374 const gchar* val = g_strdup (readbuf);
375 g_array_append_val (lines, val);
380 g_io_channel_unref (chan);
382 fprintf(stderr, "File '%s' not be read.\n", path);
389 gchar* parseenv (char* string) {
390 extern char** environ;
391 gchar* tmpstr = NULL;
395 while (environ[i] != NULL) {
396 gchar** env = g_strsplit (environ[i], "=", 2);
397 gchar* envname = g_strconcat ("$", env[0], NULL);
399 if (g_strrstr (string, envname) != NULL) {
400 tmpstr = g_strdup(string);
402 string = str_replace(envname, env[1], tmpstr);
407 g_strfreev (env); // somebody said this breaks uzbl
415 setup_signal(int signr, sigfunc *shandler) {
416 struct sigaction nh, oh;
418 nh.sa_handler = shandler;
419 sigemptyset(&nh.sa_mask);
422 if(sigaction(signr, &nh, &oh) < 0)
430 if (uzbl.behave.fifo_dir)
431 unlink (uzbl.comm.fifo_path);
432 if (uzbl.behave.socket_dir)
433 unlink (uzbl.comm.socket_path);
435 g_free(uzbl.state.executable_path);
436 g_string_free(uzbl.state.keycmd, TRUE);
437 g_hash_table_destroy(uzbl.bindings);
438 g_hash_table_destroy(uzbl.behave.commands);
441 /* used for html_mode_timeout
442 * be sure to extend this function to use
443 * more timers if needed in other places
446 set_timeout(int seconds) {
448 memset(&t, 0, sizeof t);
450 t.it_value.tv_sec = seconds;
451 t.it_value.tv_usec = 0;
452 setitimer(ITIMER_REAL, &t, NULL);
455 /* --- SIGNAL HANDLER --- */
458 catch_sigterm(int s) {
464 catch_sigint(int s) {
474 set_var_value("mode", "0");
479 /* --- CALLBACKS --- */
482 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
485 (void) navigation_action;
486 (void) policy_decision;
488 const gchar* uri = webkit_network_request_get_uri (request);
489 if (uzbl.state.verbose)
490 printf("New window requested -> %s \n", uri);
491 new_window_load_uri(uri);
496 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
501 /* If we can display it, let's display it... */
502 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
503 webkit_web_policy_decision_use (policy_decision);
507 /* ...everything we can't displayed is downloaded */
508 webkit_web_policy_decision_download (policy_decision);
513 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
517 if (uzbl.state.selected_url != NULL) {
518 if (uzbl.state.verbose)
519 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
520 new_window_load_uri(uzbl.state.selected_url);
522 if (uzbl.state.verbose)
523 printf("New web view -> %s\n","Nothing to open, exiting");
529 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
532 if (uzbl.behave.download_handler) {
533 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
534 if (uzbl.state.verbose)
535 printf("Download -> %s\n",uri);
536 /* if urls not escaped, we may have to escape and quote uri before this call */
537 run_handler(uzbl.behave.download_handler, uri);
542 /* scroll a bar in a given direction */
544 scroll (GtkAdjustment* bar, GArray *argv) {
548 gdouble page_size = gtk_adjustment_get_page_size(bar);
549 gdouble value = gtk_adjustment_get_value(bar);
550 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
553 value += page_size * amount * 0.01;
557 max_value = gtk_adjustment_get_upper(bar) - page_size;
559 if (value > max_value)
560 value = max_value; /* don't scroll past the end of the page */
562 gtk_adjustment_set_value (bar, value);
566 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
567 (void) page; (void) argv; (void) result;
568 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
572 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
573 (void) page; (void) argv; (void) result;
574 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
575 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
579 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
580 (void) page; (void) result;
581 scroll(uzbl.gui.bar_v, argv);
585 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
586 (void) page; (void) result;
587 scroll(uzbl.gui.bar_h, argv);
592 if (!uzbl.behave.show_status) {
593 gtk_widget_hide(uzbl.gui.mainbar);
595 gtk_widget_show(uzbl.gui.mainbar);
601 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
606 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
610 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
615 if (uzbl.behave.show_status) {
616 gtk_widget_hide(uzbl.gui.mainbar);
618 gtk_widget_show(uzbl.gui.mainbar);
620 uzbl.behave.show_status = !uzbl.behave.show_status;
625 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
629 //Set selected_url state variable
630 g_free(uzbl.state.selected_url);
631 uzbl.state.selected_url = NULL;
633 uzbl.state.selected_url = g_strdup(link);
639 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
643 if (uzbl.gui.main_title)
644 g_free (uzbl.gui.main_title);
645 uzbl.gui.main_title = g_strdup (title);
650 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
653 uzbl.gui.sbar.load_progress = progress;
658 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
662 if (uzbl.behave.load_finish_handler)
663 run_handler(uzbl.behave.load_finish_handler, "");
667 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
671 uzbl.gui.sbar.load_progress = 0;
672 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
673 if (uzbl.behave.load_start_handler)
674 run_handler(uzbl.behave.load_start_handler, "");
678 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
681 g_free (uzbl.state.uri);
682 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
683 uzbl.state.uri = g_string_free (newuri, FALSE);
684 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
685 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
688 if (uzbl.behave.load_commit_handler)
689 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
693 destroy_cb (GtkWidget* widget, gpointer data) {
701 if (uzbl.behave.history_handler) {
703 struct tm * timeinfo;
706 timeinfo = localtime ( &rawtime );
707 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
708 run_handler(uzbl.behave.history_handler, date);
713 /* VIEW funcs (little webkit wrappers) */
714 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
716 VIEWFUNC(reload_bypass_cache)
717 VIEWFUNC(stop_loading)
724 /* -- command to callback/function map for things we cannot attach to any signals */
725 static struct {char *key; CommandInfo value;} cmdlist[] =
726 { /* key function no_split */
727 { "back", {view_go_back, 0} },
728 { "forward", {view_go_forward, 0} },
729 { "scroll_vert", {scroll_vert, 0} },
730 { "scroll_horz", {scroll_horz, 0} },
731 { "scroll_begin", {scroll_begin, 0} },
732 { "scroll_end", {scroll_end, 0} },
733 { "reload", {view_reload, 0}, },
734 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
735 { "stop", {view_stop_loading, 0}, },
736 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
737 { "zoom_out", {view_zoom_out, 0}, },
738 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
739 { "uri", {load_uri, TRUE} },
740 { "js", {run_js, TRUE} },
741 { "script", {run_external_js, 0} },
742 { "toggle_status", {toggle_status_cb, 0} },
743 { "spawn", {spawn, 0} },
744 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
745 { "sh", {spawn_sh, 0} },
746 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
747 { "exit", {close_uzbl, 0} },
748 { "search", {search_forward_text, TRUE} },
749 { "search_reverse", {search_reverse_text, TRUE} },
750 { "dehilight", {dehilight, 0} },
751 { "toggle_insert_mode", {toggle_insert_mode, 0} },
752 { "set", {set_var, TRUE} },
753 //{ "get", {get_var, TRUE} },
754 { "bind", {act_bind, TRUE} },
755 { "dump_config", {act_dump_config, 0} },
756 { "keycmd", {keycmd, TRUE} },
757 { "keycmd_nl", {keycmd_nl, TRUE} },
758 { "keycmd_bs", {keycmd_bs, 0} },
759 { "chain", {chain, 0} },
760 { "print", {print, TRUE} }
767 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
769 for (i = 0; i < LENGTH(cmdlist); i++)
770 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
773 /* -- CORE FUNCTIONS -- */
776 free_action(gpointer act) {
777 Action *action = (Action*)act;
778 g_free(action->name);
780 g_free(action->param);
785 new_action(const gchar *name, const gchar *param) {
786 Action *action = g_new(Action, 1);
788 action->name = g_strdup(name);
790 action->param = g_strdup(param);
792 action->param = NULL;
798 file_exists (const char * filename) {
799 return (access(filename, F_OK) == 0);
803 set_var(WebKitWebView *page, GArray *argv, GString *result) {
804 (void) page; (void) result;
805 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
806 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
807 set_var_value(g_strstrip(split[0]), value);
813 print(WebKitWebView *page, GArray *argv, GString *result) {
814 (void) page; (void) result;
817 buf = expand(argv_idx(argv, 0), 0);
818 g_string_assign(result, buf);
823 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
824 (void) page; (void) result;
825 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
826 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
827 add_binding(g_strstrip(split[0]), value);
839 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
840 (void) page; (void) result;
842 if (argv_idx(argv, 0)) {
843 if (strcmp (argv_idx(argv, 0), "0") == 0) {
844 uzbl.behave.insert_mode = FALSE;
846 uzbl.behave.insert_mode = TRUE;
849 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
856 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
859 if (argv_idx(argv, 0)) {
860 GString* newuri = g_string_new (argv_idx(argv, 0));
861 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
862 run_js(web_view, argv, NULL);
865 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
866 g_string_prepend (newuri, "http://");
867 /* if we do handle cookies, ask our handler for them */
868 webkit_web_view_load_uri (web_view, newuri->str);
869 g_string_free (newuri, TRUE);
877 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
878 size_t argumentCount, const JSValueRef arguments[],
879 JSValueRef* exception) {
884 JSStringRef js_result_string;
885 GString *result = g_string_new("");
887 if (argumentCount >= 1) {
888 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
889 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
890 char ctl_line[arg_size];
891 JSStringGetUTF8CString(arg, ctl_line, arg_size);
893 parse_cmd_line(ctl_line, result);
895 JSStringRelease(arg);
897 js_result_string = JSStringCreateWithUTF8CString(result->str);
899 g_string_free(result, TRUE);
901 return JSValueMakeString(ctx, js_result_string);
904 static JSStaticFunction js_static_functions[] = {
905 {"run", js_run_command, kJSPropertyAttributeNone},
910 /* This function creates the class and its definition, only once */
911 if (!uzbl.js.initialized) {
912 /* it would be pretty cool to make this dynamic */
913 uzbl.js.classdef = kJSClassDefinitionEmpty;
914 uzbl.js.classdef.staticFunctions = js_static_functions;
916 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
922 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
923 WebKitWebFrame *frame;
924 JSGlobalContextRef context;
925 JSObjectRef globalobject;
926 JSStringRef var_name;
928 JSStringRef js_script;
929 JSValueRef js_result;
930 JSStringRef js_result_string;
931 size_t js_result_size;
935 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
936 context = webkit_web_frame_get_global_context(frame);
937 globalobject = JSContextGetGlobalObject(context);
939 /* uzbl javascript namespace */
940 var_name = JSStringCreateWithUTF8CString("Uzbl");
941 JSObjectSetProperty(context, globalobject, var_name,
942 JSObjectMake(context, uzbl.js.classref, NULL),
943 kJSClassAttributeNone, NULL);
945 /* evaluate the script and get return value*/
946 js_script = JSStringCreateWithUTF8CString(script);
947 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
948 if (js_result && !JSValueIsUndefined(context, js_result)) {
949 js_result_string = JSValueToStringCopy(context, js_result, NULL);
950 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
952 if (js_result_size) {
953 char js_result_utf8[js_result_size];
954 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
955 g_string_assign(result, js_result_utf8);
958 JSStringRelease(js_result_string);
962 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
964 JSStringRelease(var_name);
965 JSStringRelease(js_script);
969 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
971 if (argv_idx(argv, 0))
972 eval_js(web_view, argv_idx(argv, 0), result);
976 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
978 if (argv_idx(argv, 0)) {
979 GArray* lines = read_file_by_line (argv_idx (argv, 0));
984 while ((line = g_array_index(lines, gchar*, i))) {
986 js = g_strdup (line);
988 gchar* newjs = g_strconcat (js, line, NULL);
995 if (uzbl.state.verbose)
996 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
998 if (argv_idx (argv, 1)) {
999 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1003 eval_js (web_view, js, result);
1005 g_array_free (lines, TRUE);
1010 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1011 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1012 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1013 webkit_web_view_unmark_text_matches (page);
1014 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1015 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1019 if (uzbl.state.searchtx) {
1020 if (uzbl.state.verbose)
1021 printf ("Searching: %s\n", uzbl.state.searchtx);
1022 webkit_web_view_set_highlight_text_matches (page, TRUE);
1023 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1028 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1030 search_text(page, argv, TRUE);
1034 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1036 search_text(page, argv, FALSE);
1040 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1041 (void) argv; (void) result;
1042 webkit_web_view_set_highlight_text_matches (page, FALSE);
1047 new_window_load_uri (const gchar * uri) {
1048 GString* to_execute = g_string_new ("");
1049 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1051 for (i = 0; entries[i].long_name != NULL; i++) {
1052 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1053 gchar** str = (gchar**)entries[i].arg_data;
1055 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1059 if (uzbl.state.verbose)
1060 printf("\n%s\n", to_execute->str);
1061 g_spawn_command_line_async (to_execute->str, NULL);
1062 g_string_free (to_execute, TRUE);
1066 chain (WebKitWebView *page, GArray *argv, GString *result) {
1067 (void) page; (void) result;
1069 gchar **parts = NULL;
1071 while ((a = argv_idx(argv, i++))) {
1072 parts = g_strsplit (a, " ", 2);
1073 parse_command(parts[0], parts[1], result);
1079 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1083 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1089 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1093 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1099 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1104 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1106 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1111 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1118 /* --Statusbar functions-- */
1120 build_progressbar_ascii(int percent) {
1121 int width=uzbl.gui.sbar.progress_w;
1124 GString *bar = g_string_new("");
1126 l = (double)percent*((double)width/100.);
1127 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1129 for(i=0; i<(int)l; i++)
1130 g_string_append(bar, uzbl.gui.sbar.progress_s);
1133 g_string_append(bar, uzbl.gui.sbar.progress_u);
1135 return g_string_free(bar, FALSE);
1140 const GScannerConfig scan_config = {
1143 ) /* cset_skip_characters */,
1148 ) /* cset_identifier_first */,
1155 ) /* cset_identifier_nth */,
1156 ( "" ) /* cpair_comment_single */,
1158 TRUE /* case_sensitive */,
1160 FALSE /* skip_comment_multi */,
1161 FALSE /* skip_comment_single */,
1162 FALSE /* scan_comment_multi */,
1163 TRUE /* scan_identifier */,
1164 TRUE /* scan_identifier_1char */,
1165 FALSE /* scan_identifier_NULL */,
1166 TRUE /* scan_symbols */,
1167 FALSE /* scan_binary */,
1168 FALSE /* scan_octal */,
1169 FALSE /* scan_float */,
1170 FALSE /* scan_hex */,
1171 FALSE /* scan_hex_dollar */,
1172 FALSE /* scan_string_sq */,
1173 FALSE /* scan_string_dq */,
1174 TRUE /* numbers_2_int */,
1175 FALSE /* int_2_float */,
1176 FALSE /* identifier_2_string */,
1177 FALSE /* char_2_token */,
1178 FALSE /* symbol_2_token */,
1179 TRUE /* scope_0_fallback */,
1184 uzbl.scan = g_scanner_new(&scan_config);
1185 while(symp->symbol_name) {
1186 g_scanner_scope_add_symbol(uzbl.scan, 0,
1188 GINT_TO_POINTER(symp->symbol_token));
1194 expand_template(const char *template, gboolean escape_markup) {
1195 if(!template) return NULL;
1197 GTokenType token = G_TOKEN_NONE;
1198 GString *ret = g_string_new("");
1202 g_scanner_input_text(uzbl.scan, template, strlen(template));
1203 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1204 token = g_scanner_get_next_token(uzbl.scan);
1206 if(token == G_TOKEN_SYMBOL) {
1207 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1211 buf = uzbl.state.uri?
1212 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1213 g_string_append(ret, buf);
1217 g_string_append(ret, uzbl.state.uri?
1218 uzbl.state.uri:g_strdup(""));
1221 buf = itos(uzbl.gui.sbar.load_progress);
1222 g_string_append(ret, buf);
1225 case SYM_LOADPRGSBAR:
1226 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1227 g_string_append(ret, buf);
1232 buf = uzbl.gui.main_title?
1233 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1234 g_string_append(ret, buf);
1238 g_string_append(ret, uzbl.gui.main_title?
1239 uzbl.gui.main_title:g_strdup(""));
1241 case SYM_SELECTED_URI:
1243 buf = uzbl.state.selected_url?
1244 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1245 g_string_append(ret, buf);
1249 g_string_append(ret, uzbl.state.selected_url?
1250 uzbl.state.selected_url:g_strdup(""));
1253 buf = itos(uzbl.xwin);
1254 g_string_append(ret,
1255 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1260 buf = uzbl.state.keycmd->str?
1261 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1262 g_string_append(ret, buf);
1266 g_string_append(ret, uzbl.state.keycmd->str?
1267 uzbl.state.keycmd->str:g_strdup(""));
1270 g_string_append(ret,
1271 uzbl.behave.insert_mode?
1272 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1275 g_string_append(ret,
1276 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1278 /* useragent syms */
1280 buf = itos(WEBKIT_MAJOR_VERSION);
1281 g_string_append(ret, buf);
1285 buf = itos(WEBKIT_MINOR_VERSION);
1286 g_string_append(ret, buf);
1290 buf = itos(WEBKIT_MICRO_VERSION);
1291 g_string_append(ret, buf);
1295 g_string_append(ret, uzbl.state.unameinfo.sysname);
1298 g_string_append(ret, uzbl.state.unameinfo.nodename);
1301 g_string_append(ret, uzbl.state.unameinfo.release);
1304 g_string_append(ret, uzbl.state.unameinfo.version);
1307 g_string_append(ret, uzbl.state.unameinfo.machine);
1310 g_string_append(ret, ARCH);
1313 case SYM_DOMAINNAME:
1314 g_string_append(ret, uzbl.state.unameinfo.domainname);
1318 g_string_append(ret, COMMIT);
1324 else if(token == G_TOKEN_INT) {
1325 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1326 g_string_append(ret, buf);
1329 else if(token == G_TOKEN_IDENTIFIER) {
1330 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1332 else if(token == G_TOKEN_CHAR) {
1333 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1337 return g_string_free(ret, FALSE);
1339 /* --End Statusbar functions-- */
1342 sharg_append(GArray *a, const gchar *str) {
1343 const gchar *s = (str ? str : "");
1344 g_array_append_val(a, s);
1347 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1349 run_command (const gchar *command, const guint npre, const gchar **args,
1350 const gboolean sync, char **output_stdout) {
1351 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1354 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1355 gchar *pid = itos(getpid());
1356 gchar *xwin = itos(uzbl.xwin);
1358 sharg_append(a, command);
1359 for (i = 0; i < npre; i++) /* add n args before the default vars */
1360 sharg_append(a, args[i]);
1361 sharg_append(a, uzbl.state.config_file);
1362 sharg_append(a, pid);
1363 sharg_append(a, xwin);
1364 sharg_append(a, uzbl.comm.fifo_path);
1365 sharg_append(a, uzbl.comm.socket_path);
1366 sharg_append(a, uzbl.state.uri);
1367 sharg_append(a, uzbl.gui.main_title);
1369 for (i = npre; i < g_strv_length((gchar**)args); i++)
1370 sharg_append(a, args[i]);
1374 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1376 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1377 NULL, NULL, output_stdout, NULL, NULL, &err);
1378 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1379 NULL, NULL, NULL, &err);
1381 if (uzbl.state.verbose) {
1382 GString *s = g_string_new("spawned:");
1383 for (i = 0; i < (a->len); i++) {
1384 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1385 g_string_append_printf(s, " %s", qarg);
1388 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1389 printf("%s\n", s->str);
1390 g_string_free(s, TRUE);
1392 printf("Stdout: %s\n", *output_stdout);
1396 g_printerr("error on run_command: %s\n", err->message);
1401 g_array_free (a, TRUE);
1406 split_quoted(const gchar* src, const gboolean unquote) {
1407 /* split on unquoted space, return array of strings;
1408 remove a layer of quotes and backslashes if unquote */
1409 if (!src) return NULL;
1411 gboolean dq = FALSE;
1412 gboolean sq = FALSE;
1413 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1414 GString *s = g_string_new ("");
1418 for (p = src; *p != '\0'; p++) {
1419 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1420 else if (*p == '\\') { g_string_append_c(s, *p++);
1421 g_string_append_c(s, *p); }
1422 else if ((*p == '"') && unquote && !sq) dq = !dq;
1423 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1425 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1426 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1428 else if ((*p == ' ') && !dq && !sq) {
1429 dup = g_strdup(s->str);
1430 g_array_append_val(a, dup);
1431 g_string_truncate(s, 0);
1432 } else g_string_append_c(s, *p);
1434 dup = g_strdup(s->str);
1435 g_array_append_val(a, dup);
1436 ret = (gchar**)a->data;
1437 g_array_free (a, FALSE);
1438 g_string_free (s, TRUE);
1443 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1444 (void)web_view; (void)result;
1445 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1446 if (argv_idx(argv, 0))
1447 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1451 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1452 (void)web_view; (void)result;
1454 if (argv_idx(argv, 0))
1455 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1456 TRUE, &uzbl.comm.sync_stdout);
1460 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1461 (void)web_view; (void)result;
1462 if (!uzbl.behave.shell_cmd) {
1463 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1468 gchar *spacer = g_strdup("");
1469 g_array_insert_val(argv, 1, spacer);
1470 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1472 for (i = 1; i < g_strv_length(cmd); i++)
1473 g_array_prepend_val(argv, cmd[i]);
1475 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1481 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1482 (void)web_view; (void)result;
1483 if (!uzbl.behave.shell_cmd) {
1484 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1489 gchar *spacer = g_strdup("");
1490 g_array_insert_val(argv, 1, spacer);
1491 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1493 for (i = 1; i < g_strv_length(cmd); i++)
1494 g_array_prepend_val(argv, cmd[i]);
1496 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1497 TRUE, &uzbl.comm.sync_stdout);
1503 parse_command(const char *cmd, const char *param, GString *result) {
1506 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1508 gchar **par = split_quoted(param, TRUE);
1509 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1511 if (c->no_split) { /* don't split */
1512 sharg_append(a, param);
1514 for (i = 0; i < g_strv_length(par); i++)
1515 sharg_append(a, par[i]);
1518 if (result == NULL) {
1519 GString *result_print = g_string_new("");
1521 c->function(uzbl.gui.web_view, a, result_print);
1522 if (result_print->len)
1523 printf("%*s\n", result_print->len, result_print->str);
1525 g_string_free(result_print, TRUE);
1527 c->function(uzbl.gui.web_view, a, result);
1530 g_array_free (a, TRUE);
1533 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1540 if(*uzbl.net.proxy_url == ' '
1541 || uzbl.net.proxy_url == NULL) {
1542 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1543 (GType) SOUP_SESSION_PROXY_URI);
1546 suri = soup_uri_new(uzbl.net.proxy_url);
1547 g_object_set(G_OBJECT(uzbl.net.soup_session),
1548 SOUP_SESSION_PROXY_URI,
1550 soup_uri_free(suri);
1557 if(file_exists(uzbl.gui.icon)) {
1558 if (uzbl.gui.main_window)
1559 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1561 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1563 g_free (uzbl.gui.icon);
1568 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1569 g_array_append_val (a, uzbl.state.uri);
1570 load_uri(uzbl.gui.web_view, a, NULL);
1571 g_array_free (a, TRUE);
1575 cmd_always_insert_mode() {
1576 uzbl.behave.insert_mode =
1577 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1583 g_object_set(G_OBJECT(uzbl.net.soup_session),
1584 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1588 cmd_max_conns_host() {
1589 g_object_set(G_OBJECT(uzbl.net.soup_session),
1590 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1595 soup_session_remove_feature
1596 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1597 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1598 /*g_free(uzbl.net.soup_logger);*/
1600 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1601 soup_session_add_feature(uzbl.net.soup_session,
1602 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1605 static WebKitWebSettings*
1607 return webkit_web_view_get_settings(uzbl.gui.web_view);
1612 WebKitWebSettings *ws = view_settings();
1613 if (uzbl.behave.font_size > 0) {
1614 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1617 if (uzbl.behave.monospace_size > 0) {
1618 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1619 uzbl.behave.monospace_size, NULL);
1621 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1622 uzbl.behave.font_size, NULL);
1628 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1632 cmd_disable_plugins() {
1633 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1634 !uzbl.behave.disable_plugins, NULL);
1638 cmd_disable_scripts() {
1639 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1640 !uzbl.behave.disable_scripts, NULL);
1644 cmd_minimum_font_size() {
1645 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1646 uzbl.behave.minimum_font_size, NULL);
1649 cmd_autoload_img() {
1650 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1651 uzbl.behave.autoload_img, NULL);
1656 cmd_autoshrink_img() {
1657 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1658 uzbl.behave.autoshrink_img, NULL);
1663 cmd_enable_spellcheck() {
1664 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1665 uzbl.behave.enable_spellcheck, NULL);
1669 cmd_enable_private() {
1670 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1671 uzbl.behave.enable_private, NULL);
1676 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1677 uzbl.behave.print_bg, NULL);
1682 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1683 uzbl.behave.style_uri, NULL);
1687 cmd_resizable_txt() {
1688 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1689 uzbl.behave.resizable_txt, NULL);
1693 cmd_default_encoding() {
1694 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1695 uzbl.behave.default_encoding, NULL);
1699 cmd_enforce_96dpi() {
1700 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1701 uzbl.behave.enforce_96dpi, NULL);
1705 cmd_caret_browsing() {
1706 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1707 uzbl.behave.caret_browsing, NULL);
1711 cmd_cookie_handler() {
1712 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1713 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1714 if ((g_strcmp0(split[0], "sh") == 0) ||
1715 (g_strcmp0(split[0], "spawn") == 0)) {
1716 g_free (uzbl.behave.cookie_handler);
1717 uzbl.behave.cookie_handler =
1718 g_strdup_printf("sync_%s %s", split[0], split[1]);
1725 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1730 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1735 if(uzbl.behave.inject_html) {
1736 webkit_web_view_load_html_string (uzbl.gui.web_view,
1737 uzbl.behave.inject_html, NULL);
1746 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1747 uzbl.behave.modmask = 0;
1749 if(uzbl.behave.modkey)
1750 g_free(uzbl.behave.modkey);
1751 uzbl.behave.modkey = buf;
1753 for (i = 0; modkeys[i].key != NULL; i++) {
1754 if (g_strrstr(buf, modkeys[i].key))
1755 uzbl.behave.modmask |= modkeys[i].mask;
1761 if (*uzbl.net.useragent == ' ') {
1762 g_free (uzbl.net.useragent);
1763 uzbl.net.useragent = NULL;
1765 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1767 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1768 g_free(uzbl.net.useragent);
1769 uzbl.net.useragent = ua;
1775 gtk_widget_ref(uzbl.gui.scrolled_win);
1776 gtk_widget_ref(uzbl.gui.mainbar);
1777 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1778 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1780 if(uzbl.behave.status_top) {
1781 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1782 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1785 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1786 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1788 gtk_widget_unref(uzbl.gui.scrolled_win);
1789 gtk_widget_unref(uzbl.gui.mainbar);
1790 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1795 set_var_value(gchar *name, gchar *val) {
1796 uzbl_cmdprop *c = NULL;
1800 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1801 /* check for the variable type */
1802 if (c->type == TYPE_STR) {
1803 buf = expand(val, 0);
1806 } else if(c->type == TYPE_INT) {
1807 int *ip = (int *)c->ptr;
1808 buf = expand(val, 0);
1809 *ip = (int)strtoul(buf, &endp, 10);
1811 } else if (c->type == TYPE_FLOAT) {
1812 float *fp = (float *)c->ptr;
1813 buf = expand(val, 0);
1814 *fp = strtod(buf, &endp);
1818 /* invoke a command specific function */
1819 if(c->func) c->func();
1826 Behaviour *b = &uzbl.behave;
1828 if(b->html_buffer->str) {
1829 webkit_web_view_load_html_string (uzbl.gui.web_view,
1830 b->html_buffer->str, b->base_url);
1831 g_string_free(b->html_buffer, TRUE);
1832 b->html_buffer = g_string_new("");
1836 enum {M_CMD, M_HTML};
1838 parse_cmd_line(const char *ctl_line, GString *result) {
1839 Behaviour *b = &uzbl.behave;
1842 if(b->mode == M_HTML) {
1843 len = strlen(b->html_endmarker);
1844 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1845 if(len == strlen(ctl_line)-1 &&
1846 !strncmp(b->html_endmarker, ctl_line, len)) {
1848 set_var_value("mode", "0");
1853 set_timeout(b->html_timeout);
1854 g_string_append(b->html_buffer, ctl_line);
1857 else if((ctl_line[0] == '#') /* Comments */
1858 || (ctl_line[0] == ' ')
1859 || (ctl_line[0] == '\n'))
1860 ; /* ignore these lines */
1861 else { /* parse a command */
1863 gchar **tokens = NULL;
1864 len = strlen(ctl_line);
1866 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1867 ctlstrip = g_strndup(ctl_line, len - 1);
1868 else ctlstrip = g_strdup(ctl_line);
1870 tokens = g_strsplit(ctlstrip, " ", 2);
1871 parse_command(tokens[0], tokens[1], result);
1878 build_stream_name(int type, const gchar* dir) {
1879 char *xwin_str = NULL;
1880 State *s = &uzbl.state;
1883 xwin_str = itos((int)uzbl.xwin);
1885 str = g_strdup_printf
1886 ("%s/uzbl_fifo_%s", dir,
1887 s->instance_name ? s->instance_name : xwin_str);
1888 } else if (type == SOCKET) {
1889 str = g_strdup_printf
1890 ("%s/uzbl_socket_%s", dir,
1891 s->instance_name ? s->instance_name : xwin_str );
1898 control_fifo(GIOChannel *gio, GIOCondition condition) {
1899 if (uzbl.state.verbose)
1900 printf("triggered\n");
1905 if (condition & G_IO_HUP)
1906 g_error ("Fifo: Read end of pipe died!\n");
1909 g_error ("Fifo: GIOChannel broke\n");
1911 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1912 if (ret == G_IO_STATUS_ERROR) {
1913 g_error ("Fifo: Error reading: %s\n", err->message);
1917 parse_cmd_line(ctl_line, NULL);
1924 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1925 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1926 if (unlink(uzbl.comm.fifo_path) == -1)
1927 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1928 g_free(uzbl.comm.fifo_path);
1929 uzbl.comm.fifo_path = NULL;
1932 if (*dir == ' ') { /* space unsets the variable */
1937 GIOChannel *chan = NULL;
1938 GError *error = NULL;
1939 gchar *path = build_stream_name(FIFO, dir);
1941 if (!file_exists(path)) {
1942 if (mkfifo (path, 0666) == 0) {
1943 // 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.
1944 chan = g_io_channel_new_file(path, "r+", &error);
1946 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1947 if (uzbl.state.verbose)
1948 printf ("init_fifo: created successfully as %s\n", path);
1949 uzbl.comm.fifo_path = path;
1951 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1952 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1953 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1954 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1956 /* if we got this far, there was an error; cleanup */
1957 if (error) g_error_free (error);
1964 control_stdin(GIOChannel *gio, GIOCondition condition) {
1966 gchar *ctl_line = NULL;
1969 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1970 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1973 parse_cmd_line(ctl_line, NULL);
1981 GIOChannel *chan = NULL;
1982 GError *error = NULL;
1984 chan = g_io_channel_unix_new(fileno(stdin));
1986 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1987 g_error ("Stdin: could not add watch\n");
1989 if (uzbl.state.verbose)
1990 printf ("Stdin: watch added successfully\n");
1993 g_error ("Stdin: Error while opening: %s\n", error->message);
1995 if (error) g_error_free (error);
1999 control_socket(GIOChannel *chan) {
2000 struct sockaddr_un remote;
2001 unsigned int t = sizeof(remote);
2003 GIOChannel *clientchan;
2005 clientsock = accept (g_io_channel_unix_get_fd(chan),
2006 (struct sockaddr *) &remote, &t);
2008 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2009 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2010 (GIOFunc) control_client_socket, clientchan);
2017 control_client_socket(GIOChannel *clientchan) {
2019 GString *result = g_string_new("");
2020 GError *error = NULL;
2024 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2025 if (ret == G_IO_STATUS_ERROR) {
2026 g_warning ("Error reading: %s\n", error->message);
2027 g_io_channel_shutdown(clientchan, TRUE, &error);
2029 } else if (ret == G_IO_STATUS_EOF) {
2030 /* shutdown and remove channel watch from main loop */
2031 g_io_channel_shutdown(clientchan, TRUE, &error);
2036 parse_cmd_line (ctl_line, result);
2037 g_string_append_c(result, '\n');
2038 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2040 if (ret == G_IO_STATUS_ERROR) {
2041 g_warning ("Error writing: %s", error->message);
2043 g_io_channel_flush(clientchan, &error);
2046 if (error) g_error_free (error);
2047 g_string_free(result, TRUE);
2053 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2054 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2055 if (unlink(uzbl.comm.socket_path) == -1)
2056 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2057 g_free(uzbl.comm.socket_path);
2058 uzbl.comm.socket_path = NULL;
2066 GIOChannel *chan = NULL;
2068 struct sockaddr_un local;
2069 gchar *path = build_stream_name(SOCKET, dir);
2071 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2073 local.sun_family = AF_UNIX;
2074 strcpy (local.sun_path, path);
2075 unlink (local.sun_path);
2077 len = strlen (local.sun_path) + sizeof (local.sun_family);
2078 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2079 if (uzbl.state.verbose)
2080 printf ("init_socket: opened in %s\n", path);
2083 if( (chan = g_io_channel_unix_new(sock)) ) {
2084 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2085 uzbl.comm.socket_path = path;
2088 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2090 /* if we got this far, there was an error; cleanup */
2097 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2098 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2100 // this function may be called very early when the templates are not set (yet), hence the checks
2102 update_title (void) {
2103 Behaviour *b = &uzbl.behave;
2106 if (b->show_status) {
2107 if (b->title_format_short) {
2108 parsed = expand_template(b->title_format_short, FALSE);
2109 if (uzbl.gui.main_window)
2110 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2113 if (b->status_format) {
2114 parsed = expand_template(b->status_format, TRUE);
2115 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2118 if (b->status_background) {
2120 gdk_color_parse (b->status_background, &color);
2121 //labels and hboxes do not draw their own background. applying this on the vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2122 if (uzbl.gui.main_window)
2123 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2124 else if (uzbl.gui.plug)
2125 gtk_widget_modify_bg (uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2128 if (b->title_format_long) {
2129 parsed = expand_template(b->title_format_long, FALSE);
2130 if (uzbl.gui.main_window)
2131 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2138 key_press_cb (GtkWidget* window, GdkEventKey* event)
2140 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2144 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2145 || 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)
2148 /* turn off insert mode (if always_insert_mode is not used) */
2149 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2150 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2155 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2158 if (event->keyval == GDK_Escape) {
2159 g_string_truncate(uzbl.state.keycmd, 0);
2161 dehilight(uzbl.gui.web_view, NULL, NULL);
2165 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2166 if (event->keyval == GDK_Insert) {
2168 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2169 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2171 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2174 g_string_append (uzbl.state.keycmd, str);
2181 if (event->keyval == GDK_BackSpace)
2182 keycmd_bs(NULL, NULL, NULL);
2184 gboolean key_ret = FALSE;
2185 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2187 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2189 run_keycmd(key_ret);
2191 if (key_ret) return (!uzbl.behave.insert_mode);
2196 run_keycmd(const gboolean key_ret) {
2197 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2199 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2200 g_string_truncate(uzbl.state.keycmd, 0);
2201 parse_command(act->name, act->param, NULL);
2205 /* try if it's an incremental keycmd or one that takes args, and run it */
2206 GString* short_keys = g_string_new ("");
2207 GString* short_keys_inc = g_string_new ("");
2209 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2210 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2211 g_string_assign(short_keys_inc, short_keys->str);
2212 g_string_append_c(short_keys, '_');
2213 g_string_append_c(short_keys_inc, '*');
2215 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2216 /* run normal cmds only if return was pressed */
2217 exec_paramcmd(act, i);
2218 g_string_truncate(uzbl.state.keycmd, 0);
2220 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2221 if (key_ret) /* just quit the incremental command on return */
2222 g_string_truncate(uzbl.state.keycmd, 0);
2223 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2227 g_string_truncate(short_keys, short_keys->len - 1);
2229 g_string_free (short_keys, TRUE);
2230 g_string_free (short_keys_inc, TRUE);
2234 exec_paramcmd(const Action *act, const guint i) {
2235 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2236 GString *actionname = g_string_new ("");
2237 GString *actionparam = g_string_new ("");
2238 g_string_erase (parampart, 0, i+1);
2240 g_string_printf (actionname, act->name, parampart->str);
2242 g_string_printf (actionparam, act->param, parampart->str);
2243 parse_command(actionname->str, actionparam->str, NULL);
2244 g_string_free(actionname, TRUE);
2245 g_string_free(actionparam, TRUE);
2246 g_string_free(parampart, TRUE);
2254 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2255 //main_window_ref = g_object_ref(scrolled_window);
2256 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
2258 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2259 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2261 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2262 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2263 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2264 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2265 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2266 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2267 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2268 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2269 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2270 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2271 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2273 return scrolled_window;
2280 g->mainbar = gtk_hbox_new (FALSE, 0);
2282 /* keep a reference to the bar so we can re-pack it at runtime*/
2283 //sbar_ref = g_object_ref(g->mainbar);
2285 g->mainbar_label = gtk_label_new ("");
2286 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2287 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2288 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2289 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2290 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2291 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2296 GtkWidget* create_window () {
2297 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2298 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2299 gtk_widget_set_name (window, "Uzbl browser");
2300 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2301 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2307 GtkPlug* create_plug () {
2308 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2309 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2310 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2317 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2319 If actname is one that calls an external command, this function will inject
2320 newargs in front of the user-provided args in that command line. They will
2321 come become after the body of the script (in sh) or after the name of
2322 the command to execute (in spawn).
2323 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2324 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2326 The return value consist of two strings: the action (sh, ...) and its args.
2328 If act is not one that calls an external command, then the given action merely
2331 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2332 gchar *actdup = g_strdup(actname);
2333 g_array_append_val(rets, actdup);
2335 if ((g_strcmp0(actname, "spawn") == 0) ||
2336 (g_strcmp0(actname, "sh") == 0) ||
2337 (g_strcmp0(actname, "sync_spawn") == 0) ||
2338 (g_strcmp0(actname, "sync_sh") == 0)) {
2340 GString *a = g_string_new("");
2341 gchar **spawnparts = split_quoted(origargs, FALSE);
2342 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2343 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2345 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2346 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2348 g_array_append_val(rets, a->str);
2349 g_string_free(a, FALSE);
2350 g_strfreev(spawnparts);
2352 gchar *origdup = g_strdup(origargs);
2353 g_array_append_val(rets, origdup);
2355 return (gchar**)g_array_free(rets, FALSE);
2359 run_handler (const gchar *act, const gchar *args) {
2360 /* Consider this code a temporary hack to make the handlers usable.
2361 In practice, all this splicing, injection, and reconstruction is
2362 inefficient, annoying and hard to manage. Potential pitfalls arise
2363 when the handler specific args 1) are not quoted (the handler
2364 callbacks should take care of this) 2) are quoted but interfere
2365 with the users' own quotation. A more ideal solution is
2366 to refactor parse_command so that it doesn't just take a string
2367 and execute it; rather than that, we should have a function which
2368 returns the argument vector parsed from the string. This vector
2369 could be modified (e.g. insert additional args into it) before
2370 passing it to the next function that actually executes it. Though
2371 it still isn't perfect for chain actions.. will reconsider & re-
2372 factor when I have the time. -duc */
2374 char **parts = g_strsplit(act, " ", 2);
2376 if (g_strcmp0(parts[0], "chain") == 0) {
2377 GString *newargs = g_string_new("");
2378 gchar **chainparts = split_quoted(parts[1], FALSE);
2380 /* for every argument in the chain, inject the handler args
2381 and make sure the new parts are wrapped in quotes */
2382 gchar **cp = chainparts;
2384 gchar *quotless = NULL;
2385 gchar **spliced_quotless = NULL; // sigh -_-;
2386 gchar **inpart = NULL;
2389 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2391 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2392 } else quotless = g_strdup(*cp);
2394 spliced_quotless = g_strsplit(quotless, " ", 2);
2395 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2396 g_strfreev(spliced_quotless);
2398 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2404 parse_command(parts[0], &(newargs->str[1]), NULL);
2405 g_string_free(newargs, TRUE);
2406 g_strfreev(chainparts);
2409 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2410 parse_command(inparts[0], inparts[1], NULL);
2418 add_binding (const gchar *key, const gchar *act) {
2419 char **parts = g_strsplit(act, " ", 2);
2426 if (uzbl.state.verbose)
2427 printf ("Binding %-10s : %s\n", key, act);
2428 action = new_action(parts[0], parts[1]);
2430 if (g_hash_table_remove (uzbl.bindings, key))
2431 g_warning ("Overwriting existing binding for \"%s\"", key);
2432 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2437 get_xdg_var (XDG_Var xdg) {
2438 const gchar* actual_value = getenv (xdg.environmental);
2439 const gchar* home = getenv ("HOME");
2440 gchar* return_value;
2442 if (! actual_value || strcmp (actual_value, "") == 0) {
2443 if (xdg.default_value) {
2444 return_value = str_replace ("~", home, xdg.default_value);
2446 return_value = NULL;
2449 return_value = str_replace("~", home, actual_value);
2452 return return_value;
2456 find_xdg_file (int xdg_type, char* filename) {
2457 /* xdg_type = 0 => config
2458 xdg_type = 1 => data
2459 xdg_type = 2 => cache*/
2461 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2462 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2465 gchar* temporary_string;
2469 if (! file_exists (temporary_file) && xdg_type != 2) {
2470 buf = get_xdg_var (XDG[3 + xdg_type]);
2471 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2474 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2475 g_free (temporary_file);
2476 temporary_file = g_strconcat (temporary_string, filename, NULL);
2480 //g_free (temporary_string); - segfaults.
2482 if (file_exists (temporary_file)) {
2483 return temporary_file;
2490 State *s = &uzbl.state;
2491 Network *n = &uzbl.net;
2493 for (i = 0; default_config[i].command != NULL; i++) {
2494 parse_cmd_line(default_config[i].command, NULL);
2497 if (!s->config_file) {
2498 s->config_file = find_xdg_file (0, "/uzbl/config");
2501 if (s->config_file) {
2502 GArray* lines = read_file_by_line (s->config_file);
2506 while ((line = g_array_index(lines, gchar*, i))) {
2507 parse_cmd_line (line, NULL);
2511 g_array_free (lines, TRUE);
2513 if (uzbl.state.verbose)
2514 printf ("No configuration file loaded.\n");
2517 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2520 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2523 if (!uzbl.behave.cookie_handler)
2526 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2527 GString *s = g_string_new ("");
2528 SoupURI * soup_uri = soup_message_get_uri(msg);
2529 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2530 run_handler(uzbl.behave.cookie_handler, s->str);
2532 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2533 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2534 if ( p != NULL ) *p = '\0';
2535 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2537 if (uzbl.comm.sync_stdout)
2538 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2540 g_string_free(s, TRUE);
2544 save_cookies (SoupMessage *msg, gpointer user_data){
2548 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2549 cookie = soup_cookie_to_set_cookie_header(ck->data);
2550 SoupURI * soup_uri = soup_message_get_uri(msg);
2551 GString *s = g_string_new ("");
2552 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2553 run_handler(uzbl.behave.cookie_handler, s->str);
2555 g_string_free(s, TRUE);
2560 /* --- WEBINSPECTOR --- */
2562 hide_window_cb(GtkWidget *widget, gpointer data) {
2565 gtk_widget_hide(widget);
2568 static WebKitWebView*
2569 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2572 (void) web_inspector;
2573 GtkWidget* scrolled_window;
2574 GtkWidget* new_web_view;
2577 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2578 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2579 G_CALLBACK(hide_window_cb), NULL);
2581 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2582 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2583 gtk_widget_show(g->inspector_window);
2585 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2586 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2587 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2588 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2589 gtk_widget_show(scrolled_window);
2591 new_web_view = webkit_web_view_new();
2592 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2594 return WEBKIT_WEB_VIEW(new_web_view);
2598 inspector_show_window_cb (WebKitWebInspector* inspector){
2600 gtk_widget_show(uzbl.gui.inspector_window);
2604 /* TODO: Add variables and code to make use of these functions */
2606 inspector_close_window_cb (WebKitWebInspector* inspector){
2612 inspector_attach_window_cb (WebKitWebInspector* inspector){
2618 inspector_detach_window_cb (WebKitWebInspector* inspector){
2624 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2630 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2636 set_up_inspector() {
2638 WebKitWebSettings *settings = view_settings();
2639 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2641 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2642 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2643 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2644 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2645 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2646 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2647 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2649 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2653 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2655 uzbl_cmdprop *c = v;
2660 if(c->type == TYPE_STR)
2661 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2662 else if(c->type == TYPE_INT)
2663 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2667 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2671 printf("bind %s = %s %s\n", (char *)k ,
2672 (char *)a->name, a->param?(char *)a->param:"");
2677 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2678 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2683 main (int argc, char* argv[]) {
2684 gtk_init (&argc, &argv);
2685 if (!g_thread_supported ())
2686 g_thread_init (NULL);
2687 uzbl.state.executable_path = g_strdup(argv[0]);
2688 uzbl.state.selected_url = NULL;
2689 uzbl.state.searchtx = NULL;
2691 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2692 g_option_context_add_main_entries (context, entries, NULL);
2693 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2694 g_option_context_parse (context, &argc, &argv, NULL);
2695 g_option_context_free(context);
2697 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2698 gboolean verbose_override = uzbl.state.verbose;
2700 /* initialize hash table */
2701 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2703 uzbl.net.soup_session = webkit_get_default_session();
2704 uzbl.state.keycmd = g_string_new("");
2706 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2707 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2708 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2709 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2710 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2711 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2714 if(uname(&uzbl.state.unameinfo) == -1)
2715 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2717 uzbl.gui.sbar.progress_s = g_strdup("=");
2718 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2719 uzbl.gui.sbar.progress_w = 10;
2721 /* HTML mode defaults*/
2722 uzbl.behave.html_buffer = g_string_new("");
2723 uzbl.behave.html_endmarker = g_strdup(".");
2724 uzbl.behave.html_timeout = 60;
2725 uzbl.behave.base_url = g_strdup("http://invalid");
2727 /* default mode indicators */
2728 uzbl.behave.insert_indicator = g_strdup("I");
2729 uzbl.behave.cmd_indicator = g_strdup("C");
2733 make_var_to_name_hash();
2735 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2737 uzbl.gui.scrolled_win = create_browser();
2740 /* initial packing */
2741 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2742 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2744 if (uzbl.state.socket_id) {
2745 uzbl.gui.plug = create_plug ();
2746 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2747 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2749 uzbl.gui.main_window = create_window ();
2750 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2751 gtk_widget_show_all (uzbl.gui.main_window);
2752 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2755 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2757 if (uzbl.state.verbose) {
2758 printf("Uzbl start location: %s\n", argv[0]);
2759 if (uzbl.state.socket_id)
2760 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2762 printf("window_id %i\n",(int) uzbl.xwin);
2763 printf("pid %i\n", getpid ());
2764 printf("name: %s\n", uzbl.state.instance_name);
2767 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2768 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2769 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2770 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2771 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2775 if (!uzbl.behave.show_status)
2776 gtk_widget_hide(uzbl.gui.mainbar);
2785 if (verbose_override > uzbl.state.verbose)
2786 uzbl.state.verbose = verbose_override;
2789 set_var_value("uri", uri_override);
2790 g_free(uri_override);
2791 } else if (uzbl.state.uri)
2792 cmd_load_uri(uzbl.gui.web_view, NULL);
2797 return EXIT_SUCCESS;
2800 /* vi: set et ts=4: */