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, GParamSpec param_spec) {
642 const gchar *title = webkit_web_view_get_title(web_view);
643 if (uzbl.gui.main_title)
644 g_free (uzbl.gui.main_title);
645 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no 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 if (split[0] != NULL) {
807 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
808 set_var_value(g_strstrip(split[0]), value);
815 print(WebKitWebView *page, GArray *argv, GString *result) {
816 (void) page; (void) result;
819 buf = expand(argv_idx(argv, 0), 0);
820 g_string_assign(result, buf);
825 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
826 (void) page; (void) result;
827 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
828 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
829 add_binding(g_strstrip(split[0]), value);
841 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
842 (void) page; (void) result;
844 if (argv_idx(argv, 0)) {
845 if (strcmp (argv_idx(argv, 0), "0") == 0) {
846 uzbl.behave.insert_mode = FALSE;
848 uzbl.behave.insert_mode = TRUE;
851 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
858 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
861 if (argv_idx(argv, 0)) {
862 GString* newuri = g_string_new (argv_idx(argv, 0));
863 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
864 run_js(web_view, argv, NULL);
867 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
868 g_string_prepend (newuri, "http://");
869 /* if we do handle cookies, ask our handler for them */
870 webkit_web_view_load_uri (web_view, newuri->str);
871 g_string_free (newuri, TRUE);
879 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
880 size_t argumentCount, const JSValueRef arguments[],
881 JSValueRef* exception) {
886 JSStringRef js_result_string;
887 GString *result = g_string_new("");
889 if (argumentCount >= 1) {
890 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
891 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
892 char ctl_line[arg_size];
893 JSStringGetUTF8CString(arg, ctl_line, arg_size);
895 parse_cmd_line(ctl_line, result);
897 JSStringRelease(arg);
899 js_result_string = JSStringCreateWithUTF8CString(result->str);
901 g_string_free(result, TRUE);
903 return JSValueMakeString(ctx, js_result_string);
906 static JSStaticFunction js_static_functions[] = {
907 {"run", js_run_command, kJSPropertyAttributeNone},
912 /* This function creates the class and its definition, only once */
913 if (!uzbl.js.initialized) {
914 /* it would be pretty cool to make this dynamic */
915 uzbl.js.classdef = kJSClassDefinitionEmpty;
916 uzbl.js.classdef.staticFunctions = js_static_functions;
918 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
924 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
925 WebKitWebFrame *frame;
926 JSGlobalContextRef context;
927 JSObjectRef globalobject;
928 JSStringRef var_name;
930 JSStringRef js_script;
931 JSValueRef js_result;
932 JSStringRef js_result_string;
933 size_t js_result_size;
937 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
938 context = webkit_web_frame_get_global_context(frame);
939 globalobject = JSContextGetGlobalObject(context);
941 /* uzbl javascript namespace */
942 var_name = JSStringCreateWithUTF8CString("Uzbl");
943 JSObjectSetProperty(context, globalobject, var_name,
944 JSObjectMake(context, uzbl.js.classref, NULL),
945 kJSClassAttributeNone, NULL);
947 /* evaluate the script and get return value*/
948 js_script = JSStringCreateWithUTF8CString(script);
949 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
950 if (js_result && !JSValueIsUndefined(context, js_result)) {
951 js_result_string = JSValueToStringCopy(context, js_result, NULL);
952 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
954 if (js_result_size) {
955 char js_result_utf8[js_result_size];
956 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
957 g_string_assign(result, js_result_utf8);
960 JSStringRelease(js_result_string);
964 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
966 JSStringRelease(var_name);
967 JSStringRelease(js_script);
971 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
973 if (argv_idx(argv, 0))
974 eval_js(web_view, argv_idx(argv, 0), result);
978 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
980 if (argv_idx(argv, 0)) {
981 GArray* lines = read_file_by_line (argv_idx (argv, 0));
986 while ((line = g_array_index(lines, gchar*, i))) {
988 js = g_strdup (line);
990 gchar* newjs = g_strconcat (js, line, NULL);
997 if (uzbl.state.verbose)
998 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1000 if (argv_idx (argv, 1)) {
1001 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1005 eval_js (web_view, js, result);
1007 g_array_free (lines, TRUE);
1012 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1013 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1014 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1015 webkit_web_view_unmark_text_matches (page);
1016 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1017 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1021 if (uzbl.state.searchtx) {
1022 if (uzbl.state.verbose)
1023 printf ("Searching: %s\n", uzbl.state.searchtx);
1024 webkit_web_view_set_highlight_text_matches (page, TRUE);
1025 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1030 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1032 search_text(page, argv, TRUE);
1036 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1038 search_text(page, argv, FALSE);
1042 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1043 (void) argv; (void) result;
1044 webkit_web_view_set_highlight_text_matches (page, FALSE);
1049 new_window_load_uri (const gchar * uri) {
1050 GString* to_execute = g_string_new ("");
1051 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1053 for (i = 0; entries[i].long_name != NULL; i++) {
1054 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1055 gchar** str = (gchar**)entries[i].arg_data;
1057 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1061 if (uzbl.state.verbose)
1062 printf("\n%s\n", to_execute->str);
1063 g_spawn_command_line_async (to_execute->str, NULL);
1064 g_string_free (to_execute, TRUE);
1068 chain (WebKitWebView *page, GArray *argv, GString *result) {
1069 (void) page; (void) result;
1071 gchar **parts = NULL;
1073 while ((a = argv_idx(argv, i++))) {
1074 parts = g_strsplit (a, " ", 2);
1075 parse_command(parts[0], parts[1], result);
1081 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1085 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1091 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1095 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1101 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1106 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1108 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1113 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1120 /* --Statusbar functions-- */
1122 build_progressbar_ascii(int percent) {
1123 int width=uzbl.gui.sbar.progress_w;
1126 GString *bar = g_string_new("");
1128 l = (double)percent*((double)width/100.);
1129 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1131 for(i=0; i<(int)l; i++)
1132 g_string_append(bar, uzbl.gui.sbar.progress_s);
1135 g_string_append(bar, uzbl.gui.sbar.progress_u);
1137 return g_string_free(bar, FALSE);
1142 const GScannerConfig scan_config = {
1145 ) /* cset_skip_characters */,
1150 ) /* cset_identifier_first */,
1157 ) /* cset_identifier_nth */,
1158 ( "" ) /* cpair_comment_single */,
1160 TRUE /* case_sensitive */,
1162 FALSE /* skip_comment_multi */,
1163 FALSE /* skip_comment_single */,
1164 FALSE /* scan_comment_multi */,
1165 TRUE /* scan_identifier */,
1166 TRUE /* scan_identifier_1char */,
1167 FALSE /* scan_identifier_NULL */,
1168 TRUE /* scan_symbols */,
1169 FALSE /* scan_binary */,
1170 FALSE /* scan_octal */,
1171 FALSE /* scan_float */,
1172 FALSE /* scan_hex */,
1173 FALSE /* scan_hex_dollar */,
1174 FALSE /* scan_string_sq */,
1175 FALSE /* scan_string_dq */,
1176 TRUE /* numbers_2_int */,
1177 FALSE /* int_2_float */,
1178 FALSE /* identifier_2_string */,
1179 FALSE /* char_2_token */,
1180 FALSE /* symbol_2_token */,
1181 TRUE /* scope_0_fallback */,
1186 uzbl.scan = g_scanner_new(&scan_config);
1187 while(symp->symbol_name) {
1188 g_scanner_scope_add_symbol(uzbl.scan, 0,
1190 GINT_TO_POINTER(symp->symbol_token));
1196 expand_template(const char *template, gboolean escape_markup) {
1197 if(!template) return NULL;
1199 GTokenType token = G_TOKEN_NONE;
1200 GString *ret = g_string_new("");
1204 g_scanner_input_text(uzbl.scan, template, strlen(template));
1205 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1206 token = g_scanner_get_next_token(uzbl.scan);
1208 if(token == G_TOKEN_SYMBOL) {
1209 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1213 buf = uzbl.state.uri?
1214 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1215 g_string_append(ret, buf);
1219 g_string_append(ret, uzbl.state.uri?
1220 uzbl.state.uri:g_strdup(""));
1223 buf = itos(uzbl.gui.sbar.load_progress);
1224 g_string_append(ret, buf);
1227 case SYM_LOADPRGSBAR:
1228 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1229 g_string_append(ret, buf);
1234 buf = uzbl.gui.main_title?
1235 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1236 g_string_append(ret, buf);
1240 g_string_append(ret, uzbl.gui.main_title?
1241 uzbl.gui.main_title:g_strdup(""));
1243 case SYM_SELECTED_URI:
1245 buf = uzbl.state.selected_url?
1246 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1247 g_string_append(ret, buf);
1251 g_string_append(ret, uzbl.state.selected_url?
1252 uzbl.state.selected_url:g_strdup(""));
1255 buf = itos(uzbl.xwin);
1256 g_string_append(ret,
1257 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1262 buf = uzbl.state.keycmd->str?
1263 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1264 g_string_append(ret, buf);
1268 g_string_append(ret, uzbl.state.keycmd->str?
1269 uzbl.state.keycmd->str:g_strdup(""));
1272 g_string_append(ret,
1273 uzbl.behave.insert_mode?
1274 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1277 g_string_append(ret,
1278 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1280 /* useragent syms */
1282 buf = itos(WEBKIT_MAJOR_VERSION);
1283 g_string_append(ret, buf);
1287 buf = itos(WEBKIT_MINOR_VERSION);
1288 g_string_append(ret, buf);
1292 buf = itos(WEBKIT_MICRO_VERSION);
1293 g_string_append(ret, buf);
1297 g_string_append(ret, uzbl.state.unameinfo.sysname);
1300 g_string_append(ret, uzbl.state.unameinfo.nodename);
1303 g_string_append(ret, uzbl.state.unameinfo.release);
1306 g_string_append(ret, uzbl.state.unameinfo.version);
1309 g_string_append(ret, uzbl.state.unameinfo.machine);
1312 g_string_append(ret, ARCH);
1315 case SYM_DOMAINNAME:
1316 g_string_append(ret, uzbl.state.unameinfo.domainname);
1320 g_string_append(ret, COMMIT);
1326 else if(token == G_TOKEN_INT) {
1327 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1328 g_string_append(ret, buf);
1331 else if(token == G_TOKEN_IDENTIFIER) {
1332 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1334 else if(token == G_TOKEN_CHAR) {
1335 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1339 return g_string_free(ret, FALSE);
1341 /* --End Statusbar functions-- */
1344 sharg_append(GArray *a, const gchar *str) {
1345 const gchar *s = (str ? str : "");
1346 g_array_append_val(a, s);
1349 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1351 run_command (const gchar *command, const guint npre, const gchar **args,
1352 const gboolean sync, char **output_stdout) {
1353 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1356 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1357 gchar *pid = itos(getpid());
1358 gchar *xwin = itos(uzbl.xwin);
1360 sharg_append(a, command);
1361 for (i = 0; i < npre; i++) /* add n args before the default vars */
1362 sharg_append(a, args[i]);
1363 sharg_append(a, uzbl.state.config_file);
1364 sharg_append(a, pid);
1365 sharg_append(a, xwin);
1366 sharg_append(a, uzbl.comm.fifo_path);
1367 sharg_append(a, uzbl.comm.socket_path);
1368 sharg_append(a, uzbl.state.uri);
1369 sharg_append(a, uzbl.gui.main_title);
1371 for (i = npre; i < g_strv_length((gchar**)args); i++)
1372 sharg_append(a, args[i]);
1376 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1378 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1379 NULL, NULL, output_stdout, NULL, NULL, &err);
1380 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1381 NULL, NULL, NULL, &err);
1383 if (uzbl.state.verbose) {
1384 GString *s = g_string_new("spawned:");
1385 for (i = 0; i < (a->len); i++) {
1386 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1387 g_string_append_printf(s, " %s", qarg);
1390 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1391 printf("%s\n", s->str);
1392 g_string_free(s, TRUE);
1394 printf("Stdout: %s\n", *output_stdout);
1398 g_printerr("error on run_command: %s\n", err->message);
1403 g_array_free (a, TRUE);
1408 split_quoted(const gchar* src, const gboolean unquote) {
1409 /* split on unquoted space, return array of strings;
1410 remove a layer of quotes and backslashes if unquote */
1411 if (!src) return NULL;
1413 gboolean dq = FALSE;
1414 gboolean sq = FALSE;
1415 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1416 GString *s = g_string_new ("");
1420 for (p = src; *p != '\0'; p++) {
1421 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1422 else if (*p == '\\') { g_string_append_c(s, *p++);
1423 g_string_append_c(s, *p); }
1424 else if ((*p == '"') && unquote && !sq) dq = !dq;
1425 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1427 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1428 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1430 else if ((*p == ' ') && !dq && !sq) {
1431 dup = g_strdup(s->str);
1432 g_array_append_val(a, dup);
1433 g_string_truncate(s, 0);
1434 } else g_string_append_c(s, *p);
1436 dup = g_strdup(s->str);
1437 g_array_append_val(a, dup);
1438 ret = (gchar**)a->data;
1439 g_array_free (a, FALSE);
1440 g_string_free (s, TRUE);
1445 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1446 (void)web_view; (void)result;
1447 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1448 if (argv_idx(argv, 0))
1449 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1453 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1454 (void)web_view; (void)result;
1456 if (argv_idx(argv, 0))
1457 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1458 TRUE, &uzbl.comm.sync_stdout);
1462 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1463 (void)web_view; (void)result;
1464 if (!uzbl.behave.shell_cmd) {
1465 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1470 gchar *spacer = g_strdup("");
1471 g_array_insert_val(argv, 1, spacer);
1472 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1474 for (i = 1; i < g_strv_length(cmd); i++)
1475 g_array_prepend_val(argv, cmd[i]);
1477 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1483 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1484 (void)web_view; (void)result;
1485 if (!uzbl.behave.shell_cmd) {
1486 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1491 gchar *spacer = g_strdup("");
1492 g_array_insert_val(argv, 1, spacer);
1493 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1495 for (i = 1; i < g_strv_length(cmd); i++)
1496 g_array_prepend_val(argv, cmd[i]);
1498 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1499 TRUE, &uzbl.comm.sync_stdout);
1505 parse_command(const char *cmd, const char *param, GString *result) {
1508 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1510 gchar **par = split_quoted(param, TRUE);
1511 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1513 if (c->no_split) { /* don't split */
1514 sharg_append(a, param);
1516 for (i = 0; i < g_strv_length(par); i++)
1517 sharg_append(a, par[i]);
1520 if (result == NULL) {
1521 GString *result_print = g_string_new("");
1523 c->function(uzbl.gui.web_view, a, result_print);
1524 if (result_print->len)
1525 printf("%*s\n", result_print->len, result_print->str);
1527 g_string_free(result_print, TRUE);
1529 c->function(uzbl.gui.web_view, a, result);
1532 g_array_free (a, TRUE);
1535 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1542 if(*uzbl.net.proxy_url == ' '
1543 || uzbl.net.proxy_url == NULL) {
1544 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1545 (GType) SOUP_SESSION_PROXY_URI);
1548 suri = soup_uri_new(uzbl.net.proxy_url);
1549 g_object_set(G_OBJECT(uzbl.net.soup_session),
1550 SOUP_SESSION_PROXY_URI,
1552 soup_uri_free(suri);
1559 if(file_exists(uzbl.gui.icon)) {
1560 if (uzbl.gui.main_window)
1561 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1563 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1565 g_free (uzbl.gui.icon);
1570 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1571 g_array_append_val (a, uzbl.state.uri);
1572 load_uri(uzbl.gui.web_view, a, NULL);
1573 g_array_free (a, TRUE);
1577 cmd_always_insert_mode() {
1578 uzbl.behave.insert_mode =
1579 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1585 g_object_set(G_OBJECT(uzbl.net.soup_session),
1586 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1590 cmd_max_conns_host() {
1591 g_object_set(G_OBJECT(uzbl.net.soup_session),
1592 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1597 soup_session_remove_feature
1598 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1599 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1600 /*g_free(uzbl.net.soup_logger);*/
1602 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1603 soup_session_add_feature(uzbl.net.soup_session,
1604 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1607 static WebKitWebSettings*
1609 return webkit_web_view_get_settings(uzbl.gui.web_view);
1614 WebKitWebSettings *ws = view_settings();
1615 if (uzbl.behave.font_size > 0) {
1616 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1619 if (uzbl.behave.monospace_size > 0) {
1620 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1621 uzbl.behave.monospace_size, NULL);
1623 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1624 uzbl.behave.font_size, NULL);
1630 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1634 cmd_disable_plugins() {
1635 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1636 !uzbl.behave.disable_plugins, NULL);
1640 cmd_disable_scripts() {
1641 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1642 !uzbl.behave.disable_scripts, NULL);
1646 cmd_minimum_font_size() {
1647 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1648 uzbl.behave.minimum_font_size, NULL);
1651 cmd_autoload_img() {
1652 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1653 uzbl.behave.autoload_img, NULL);
1658 cmd_autoshrink_img() {
1659 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1660 uzbl.behave.autoshrink_img, NULL);
1665 cmd_enable_spellcheck() {
1666 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1667 uzbl.behave.enable_spellcheck, NULL);
1671 cmd_enable_private() {
1672 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1673 uzbl.behave.enable_private, NULL);
1678 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1679 uzbl.behave.print_bg, NULL);
1684 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1685 uzbl.behave.style_uri, NULL);
1689 cmd_resizable_txt() {
1690 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1691 uzbl.behave.resizable_txt, NULL);
1695 cmd_default_encoding() {
1696 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1697 uzbl.behave.default_encoding, NULL);
1701 cmd_enforce_96dpi() {
1702 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1703 uzbl.behave.enforce_96dpi, NULL);
1707 cmd_caret_browsing() {
1708 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1709 uzbl.behave.caret_browsing, NULL);
1713 cmd_cookie_handler() {
1714 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1715 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1716 if ((g_strcmp0(split[0], "sh") == 0) ||
1717 (g_strcmp0(split[0], "spawn") == 0)) {
1718 g_free (uzbl.behave.cookie_handler);
1719 uzbl.behave.cookie_handler =
1720 g_strdup_printf("sync_%s %s", split[0], split[1]);
1727 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1732 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1737 if(uzbl.behave.inject_html) {
1738 webkit_web_view_load_html_string (uzbl.gui.web_view,
1739 uzbl.behave.inject_html, NULL);
1748 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1749 uzbl.behave.modmask = 0;
1751 if(uzbl.behave.modkey)
1752 g_free(uzbl.behave.modkey);
1753 uzbl.behave.modkey = buf;
1755 for (i = 0; modkeys[i].key != NULL; i++) {
1756 if (g_strrstr(buf, modkeys[i].key))
1757 uzbl.behave.modmask |= modkeys[i].mask;
1763 if (*uzbl.net.useragent == ' ') {
1764 g_free (uzbl.net.useragent);
1765 uzbl.net.useragent = NULL;
1767 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1769 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1770 g_free(uzbl.net.useragent);
1771 uzbl.net.useragent = ua;
1777 gtk_widget_ref(uzbl.gui.scrolled_win);
1778 gtk_widget_ref(uzbl.gui.mainbar);
1779 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1780 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1782 if(uzbl.behave.status_top) {
1783 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1784 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1787 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1788 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1790 gtk_widget_unref(uzbl.gui.scrolled_win);
1791 gtk_widget_unref(uzbl.gui.mainbar);
1792 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1797 set_var_value(gchar *name, gchar *val) {
1798 uzbl_cmdprop *c = NULL;
1802 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1803 /* check for the variable type */
1804 if (c->type == TYPE_STR) {
1805 buf = expand(val, 0);
1808 } else if(c->type == TYPE_INT) {
1809 int *ip = (int *)c->ptr;
1810 buf = expand(val, 0);
1811 *ip = (int)strtoul(buf, &endp, 10);
1813 } else if (c->type == TYPE_FLOAT) {
1814 float *fp = (float *)c->ptr;
1815 buf = expand(val, 0);
1816 *fp = strtod(buf, &endp);
1820 /* invoke a command specific function */
1821 if(c->func) c->func();
1828 Behaviour *b = &uzbl.behave;
1830 if(b->html_buffer->str) {
1831 webkit_web_view_load_html_string (uzbl.gui.web_view,
1832 b->html_buffer->str, b->base_url);
1833 g_string_free(b->html_buffer, TRUE);
1834 b->html_buffer = g_string_new("");
1838 enum {M_CMD, M_HTML};
1840 parse_cmd_line(const char *ctl_line, GString *result) {
1841 Behaviour *b = &uzbl.behave;
1844 if(b->mode == M_HTML) {
1845 len = strlen(b->html_endmarker);
1846 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1847 if(len == strlen(ctl_line)-1 &&
1848 !strncmp(b->html_endmarker, ctl_line, len)) {
1850 set_var_value("mode", "0");
1855 set_timeout(b->html_timeout);
1856 g_string_append(b->html_buffer, ctl_line);
1859 else if((ctl_line[0] == '#') /* Comments */
1860 || (ctl_line[0] == ' ')
1861 || (ctl_line[0] == '\n'))
1862 ; /* ignore these lines */
1863 else { /* parse a command */
1865 gchar **tokens = NULL;
1866 len = strlen(ctl_line);
1868 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1869 ctlstrip = g_strndup(ctl_line, len - 1);
1870 else ctlstrip = g_strdup(ctl_line);
1872 tokens = g_strsplit(ctlstrip, " ", 2);
1873 parse_command(tokens[0], tokens[1], result);
1880 build_stream_name(int type, const gchar* dir) {
1881 char *xwin_str = NULL;
1882 State *s = &uzbl.state;
1885 xwin_str = itos((int)uzbl.xwin);
1887 str = g_strdup_printf
1888 ("%s/uzbl_fifo_%s", dir,
1889 s->instance_name ? s->instance_name : xwin_str);
1890 } else if (type == SOCKET) {
1891 str = g_strdup_printf
1892 ("%s/uzbl_socket_%s", dir,
1893 s->instance_name ? s->instance_name : xwin_str );
1900 control_fifo(GIOChannel *gio, GIOCondition condition) {
1901 if (uzbl.state.verbose)
1902 printf("triggered\n");
1907 if (condition & G_IO_HUP)
1908 g_error ("Fifo: Read end of pipe died!\n");
1911 g_error ("Fifo: GIOChannel broke\n");
1913 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1914 if (ret == G_IO_STATUS_ERROR) {
1915 g_error ("Fifo: Error reading: %s\n", err->message);
1919 parse_cmd_line(ctl_line, NULL);
1926 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1927 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1928 if (unlink(uzbl.comm.fifo_path) == -1)
1929 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1930 g_free(uzbl.comm.fifo_path);
1931 uzbl.comm.fifo_path = NULL;
1934 if (*dir == ' ') { /* space unsets the variable */
1939 GIOChannel *chan = NULL;
1940 GError *error = NULL;
1941 gchar *path = build_stream_name(FIFO, dir);
1943 if (!file_exists(path)) {
1944 if (mkfifo (path, 0666) == 0) {
1945 // 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.
1946 chan = g_io_channel_new_file(path, "r+", &error);
1948 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1949 if (uzbl.state.verbose)
1950 printf ("init_fifo: created successfully as %s\n", path);
1951 uzbl.comm.fifo_path = path;
1953 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1954 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1955 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1956 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1958 /* if we got this far, there was an error; cleanup */
1959 if (error) g_error_free (error);
1966 control_stdin(GIOChannel *gio, GIOCondition condition) {
1968 gchar *ctl_line = NULL;
1971 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1972 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1975 parse_cmd_line(ctl_line, NULL);
1983 GIOChannel *chan = NULL;
1984 GError *error = NULL;
1986 chan = g_io_channel_unix_new(fileno(stdin));
1988 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1989 g_error ("Stdin: could not add watch\n");
1991 if (uzbl.state.verbose)
1992 printf ("Stdin: watch added successfully\n");
1995 g_error ("Stdin: Error while opening: %s\n", error->message);
1997 if (error) g_error_free (error);
2001 control_socket(GIOChannel *chan) {
2002 struct sockaddr_un remote;
2003 unsigned int t = sizeof(remote);
2005 GIOChannel *clientchan;
2007 clientsock = accept (g_io_channel_unix_get_fd(chan),
2008 (struct sockaddr *) &remote, &t);
2010 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2011 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2012 (GIOFunc) control_client_socket, clientchan);
2019 control_client_socket(GIOChannel *clientchan) {
2021 GString *result = g_string_new("");
2022 GError *error = NULL;
2026 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2027 if (ret == G_IO_STATUS_ERROR) {
2028 g_warning ("Error reading: %s\n", error->message);
2029 g_io_channel_shutdown(clientchan, TRUE, &error);
2031 } else if (ret == G_IO_STATUS_EOF) {
2032 /* shutdown and remove channel watch from main loop */
2033 g_io_channel_shutdown(clientchan, TRUE, &error);
2038 parse_cmd_line (ctl_line, result);
2039 g_string_append_c(result, '\n');
2040 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2042 if (ret == G_IO_STATUS_ERROR) {
2043 g_warning ("Error writing: %s", error->message);
2045 g_io_channel_flush(clientchan, &error);
2048 if (error) g_error_free (error);
2049 g_string_free(result, TRUE);
2055 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2056 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2057 if (unlink(uzbl.comm.socket_path) == -1)
2058 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2059 g_free(uzbl.comm.socket_path);
2060 uzbl.comm.socket_path = NULL;
2068 GIOChannel *chan = NULL;
2070 struct sockaddr_un local;
2071 gchar *path = build_stream_name(SOCKET, dir);
2073 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2075 local.sun_family = AF_UNIX;
2076 strcpy (local.sun_path, path);
2077 unlink (local.sun_path);
2079 len = strlen (local.sun_path) + sizeof (local.sun_family);
2080 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2081 if (uzbl.state.verbose)
2082 printf ("init_socket: opened in %s\n", path);
2085 if( (chan = g_io_channel_unix_new(sock)) ) {
2086 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2087 uzbl.comm.socket_path = path;
2090 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2092 /* if we got this far, there was an error; cleanup */
2099 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2100 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2102 // this function may be called very early when the templates are not set (yet), hence the checks
2104 update_title (void) {
2105 Behaviour *b = &uzbl.behave;
2108 if (b->show_status) {
2109 if (b->title_format_short) {
2110 parsed = expand_template(b->title_format_short, FALSE);
2111 if (uzbl.gui.main_window)
2112 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2115 if (b->status_format) {
2116 parsed = expand_template(b->status_format, TRUE);
2117 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2120 if (b->status_background) {
2122 gdk_color_parse (b->status_background, &color);
2123 //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)
2124 if (uzbl.gui.main_window)
2125 gtk_widget_modify_bg (uzbl.gui.main_window, 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), "notify::title", G_CALLBACK (title_change_cb), NULL);
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 (g_strcmp0(s->config_file, "-") == 0) {
2498 s->config_file = NULL;
2502 if (!s->config_file) {
2503 s->config_file = find_xdg_file (0, "/uzbl/config");
2506 if (s->config_file) {
2507 GArray* lines = read_file_by_line (s->config_file);
2511 while ((line = g_array_index(lines, gchar*, i))) {
2512 parse_cmd_line (line, NULL);
2516 g_array_free (lines, TRUE);
2518 if (uzbl.state.verbose)
2519 printf ("No configuration file loaded.\n");
2522 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2525 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2528 if (!uzbl.behave.cookie_handler)
2531 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2532 GString *s = g_string_new ("");
2533 SoupURI * soup_uri = soup_message_get_uri(msg);
2534 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2535 run_handler(uzbl.behave.cookie_handler, s->str);
2537 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2538 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2539 if ( p != NULL ) *p = '\0';
2540 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2542 if (uzbl.comm.sync_stdout)
2543 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2545 g_string_free(s, TRUE);
2549 save_cookies (SoupMessage *msg, gpointer user_data){
2553 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2554 cookie = soup_cookie_to_set_cookie_header(ck->data);
2555 SoupURI * soup_uri = soup_message_get_uri(msg);
2556 GString *s = g_string_new ("");
2557 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2558 run_handler(uzbl.behave.cookie_handler, s->str);
2560 g_string_free(s, TRUE);
2565 /* --- WEBINSPECTOR --- */
2567 hide_window_cb(GtkWidget *widget, gpointer data) {
2570 gtk_widget_hide(widget);
2573 static WebKitWebView*
2574 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2577 (void) web_inspector;
2578 GtkWidget* scrolled_window;
2579 GtkWidget* new_web_view;
2582 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2583 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2584 G_CALLBACK(hide_window_cb), NULL);
2586 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2587 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2588 gtk_widget_show(g->inspector_window);
2590 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2591 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2592 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2593 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2594 gtk_widget_show(scrolled_window);
2596 new_web_view = webkit_web_view_new();
2597 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2599 return WEBKIT_WEB_VIEW(new_web_view);
2603 inspector_show_window_cb (WebKitWebInspector* inspector){
2605 gtk_widget_show(uzbl.gui.inspector_window);
2609 /* TODO: Add variables and code to make use of these functions */
2611 inspector_close_window_cb (WebKitWebInspector* inspector){
2617 inspector_attach_window_cb (WebKitWebInspector* inspector){
2623 inspector_detach_window_cb (WebKitWebInspector* inspector){
2629 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2635 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2641 set_up_inspector() {
2643 WebKitWebSettings *settings = view_settings();
2644 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2646 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2647 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2648 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2649 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2650 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2651 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2652 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2654 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2658 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2660 uzbl_cmdprop *c = v;
2665 if(c->type == TYPE_STR)
2666 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2667 else if(c->type == TYPE_INT)
2668 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2672 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2676 printf("bind %s = %s %s\n", (char *)k ,
2677 (char *)a->name, a->param?(char *)a->param:"");
2682 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2683 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2688 main (int argc, char* argv[]) {
2689 gtk_init (&argc, &argv);
2690 if (!g_thread_supported ())
2691 g_thread_init (NULL);
2692 uzbl.state.executable_path = g_strdup(argv[0]);
2693 uzbl.state.selected_url = NULL;
2694 uzbl.state.searchtx = NULL;
2696 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2697 g_option_context_add_main_entries (context, entries, NULL);
2698 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2699 g_option_context_parse (context, &argc, &argv, NULL);
2700 g_option_context_free(context);
2702 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2703 gboolean verbose_override = uzbl.state.verbose;
2705 /* initialize hash table */
2706 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2708 uzbl.net.soup_session = webkit_get_default_session();
2709 uzbl.state.keycmd = g_string_new("");
2711 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2712 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2713 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2714 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2715 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2716 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2719 if(uname(&uzbl.state.unameinfo) == -1)
2720 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2722 uzbl.gui.sbar.progress_s = g_strdup("=");
2723 uzbl.gui.sbar.progress_u = g_strdup("·");
2724 uzbl.gui.sbar.progress_w = 10;
2726 /* HTML mode defaults*/
2727 uzbl.behave.html_buffer = g_string_new("");
2728 uzbl.behave.html_endmarker = g_strdup(".");
2729 uzbl.behave.html_timeout = 60;
2730 uzbl.behave.base_url = g_strdup("http://invalid");
2732 /* default mode indicators */
2733 uzbl.behave.insert_indicator = g_strdup("I");
2734 uzbl.behave.cmd_indicator = g_strdup("C");
2738 make_var_to_name_hash();
2740 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2742 uzbl.gui.scrolled_win = create_browser();
2745 /* initial packing */
2746 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2747 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2749 if (uzbl.state.socket_id) {
2750 uzbl.gui.plug = create_plug ();
2751 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2752 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2754 uzbl.gui.main_window = create_window ();
2755 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2756 gtk_widget_show_all (uzbl.gui.main_window);
2757 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2760 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2762 if (uzbl.state.verbose) {
2763 printf("Uzbl start location: %s\n", argv[0]);
2764 if (uzbl.state.socket_id)
2765 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2767 printf("window_id %i\n",(int) uzbl.xwin);
2768 printf("pid %i\n", getpid ());
2769 printf("name: %s\n", uzbl.state.instance_name);
2772 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2773 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2774 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2775 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2776 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2780 if (!uzbl.behave.show_status)
2781 gtk_widget_hide(uzbl.gui.mainbar);
2788 if (verbose_override > uzbl.state.verbose)
2789 uzbl.state.verbose = verbose_override;
2792 set_var_value("uri", uri_override);
2793 g_free(uri_override);
2794 } else if (uzbl.state.uri)
2795 cmd_load_uri(uzbl.gui.web_view, NULL);
2800 return EXIT_SUCCESS;
2803 /* vi: set et ts=4: */