1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
39 #include <gdk/gdkkeysyms.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
44 #include <sys/utsname.h>
46 #include <webkit/webkit.h>
47 #include <libsoup/soup.h>
48 #include <JavaScriptCore/JavaScript.h>
64 /* commandline arguments (set initial values for the state variables) */
66 GOptionEntry entries[] =
68 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
69 "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
70 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
71 "Whether to print all messages or just errors.", NULL },
72 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
73 "Name of the current instance (defaults to Xorg window id)", "NAME" },
74 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
75 "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
76 { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
77 "Socket ID", "SOCKET" },
78 { NULL, 0, 0, 0, NULL, NULL, NULL }
81 /* associate command names to their properties */
82 typedef const struct {
89 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
91 /* an abbreviation to help keep the table's width humane */
92 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
97 } var_name_to_ptr[] = {
98 /* variable name pointer to variable in code type dump callback function */
99 /* --------------------------------------------------------------------------------------- */
100 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
101 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
102 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
103 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
104 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
105 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
106 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
107 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
108 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
109 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
110 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
111 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
112 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
113 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
114 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
115 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
116 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
117 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
118 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
119 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
120 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
121 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
122 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
123 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
124 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
125 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
126 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
127 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
128 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
129 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
130 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
131 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
132 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
133 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
134 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
135 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
136 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
137 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
138 /* exported WebKitWebSettings properties */
139 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
140 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
141 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
142 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
143 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
144 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
145 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
146 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
147 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
148 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
149 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
150 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
151 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
152 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
153 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
154 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
156 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
157 }, *n2v_p = var_name_to_ptr;
163 { "SHIFT", GDK_SHIFT_MASK }, // shift
164 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
165 { "CONTROL", GDK_CONTROL_MASK }, // control
166 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
167 { "MOD2", GDK_MOD2_MASK }, // 5th mod
168 { "MOD3", GDK_MOD3_MASK }, // 6th mod
169 { "MOD4", GDK_MOD4_MASK }, // 7th mod
170 { "MOD5", GDK_MOD5_MASK }, // 8th mod
171 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
172 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
173 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
174 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
175 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
176 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
177 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
178 { "META", GDK_META_MASK }, // meta (since 2.10)
183 /* construct a hash from the var_name_to_ptr array for quick access */
185 make_var_to_name_hash() {
186 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
188 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
193 /* --- UTILITY FUNCTIONS --- */
195 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
197 get_exp_type(gchar *s) {
201 else if(*(s+1) == '{')
202 return EXP_BRACED_VAR;
203 else if(*(s+1) == '<')
206 return EXP_SIMPLE_VAR;
211 /* setting 'recurse = 1' will prevent expand() from
212 * expanding '@(command)'
215 expand(char *s, gboolean recurse) {
222 gchar *cmd_stdout = NULL;
224 GString *buf = g_string_new("");
225 GString *js_ret = g_string_new("");
230 g_string_append_c(buf, *++s);
235 etype = get_exp_type(s);
253 if( (vend = strchr(s, upto)) ||
254 (vend = strchr(s, '\0')) ) {
255 strncpy(ret, s, vend-s);
259 if(etype == EXP_SIMPLE_VAR ||
260 etype == EXP_BRACED_VAR) {
261 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
262 if(c->type == TYPE_STR)
263 g_string_append(buf, (gchar *)*c->ptr);
264 else if(c->type == TYPE_INT) {
265 char *b = itos((int)*c->ptr);
266 g_string_append(buf, b);
270 if(upto == ' ') s = vend;
275 mycmd = expand(ret, 1);
276 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
280 g_printerr("error on running command: %s\n", err->message);
283 else if (*cmd_stdout) {
284 g_string_append(buf, cmd_stdout);
291 mycmd = expand(ret, 1);
292 eval_js(uzbl.gui.web_view, mycmd, js_ret);
296 g_string_append(buf, js_ret->str);
297 g_string_free(js_ret, 1);
304 g_string_append_c(buf, *s);
309 return g_string_free(buf, FALSE);
316 snprintf(tmp, sizeof(tmp), "%i", val);
317 return g_strdup(tmp);
321 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
324 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
327 str_replace (const char* search, const char* replace, const char* string) {
331 buf = g_strsplit (string, search, -1);
332 ret = g_strjoinv (replace, buf);
333 g_strfreev(buf); // somebody said this segfaults
339 read_file_by_line (gchar *path) {
340 GIOChannel *chan = NULL;
341 gchar *readbuf = NULL;
343 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
346 chan = g_io_channel_new_file(path, "r", NULL);
349 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
350 const gchar* val = g_strdup (readbuf);
351 g_array_append_val (lines, val);
356 g_io_channel_unref (chan);
358 fprintf(stderr, "File '%s' not be read.\n", path);
365 gchar* parseenv (char* string) {
366 extern char** environ;
367 gchar* tmpstr = NULL;
371 while (environ[i] != NULL) {
372 gchar** env = g_strsplit (environ[i], "=", 2);
373 gchar* envname = g_strconcat ("$", env[0], NULL);
375 if (g_strrstr (string, envname) != NULL) {
376 tmpstr = g_strdup(string);
378 string = str_replace(envname, env[1], tmpstr);
383 g_strfreev (env); // somebody said this breaks uzbl
391 setup_signal(int signr, sigfunc *shandler) {
392 struct sigaction nh, oh;
394 nh.sa_handler = shandler;
395 sigemptyset(&nh.sa_mask);
398 if(sigaction(signr, &nh, &oh) < 0)
406 if (uzbl.behave.fifo_dir)
407 unlink (uzbl.comm.fifo_path);
408 if (uzbl.behave.socket_dir)
409 unlink (uzbl.comm.socket_path);
411 g_free(uzbl.state.executable_path);
412 g_string_free(uzbl.state.keycmd, TRUE);
413 g_hash_table_destroy(uzbl.bindings);
414 g_hash_table_destroy(uzbl.behave.commands);
417 /* used for html_mode_timeout
418 * be sure to extend this function to use
419 * more timers if needed in other places
422 set_timeout(int seconds) {
424 memset(&t, 0, sizeof t);
426 t.it_value.tv_sec = seconds;
427 t.it_value.tv_usec = 0;
428 setitimer(ITIMER_REAL, &t, NULL);
431 /* --- SIGNAL HANDLER --- */
434 catch_sigterm(int s) {
440 catch_sigint(int s) {
450 set_var_value("mode", "0");
455 /* --- CALLBACKS --- */
458 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
461 (void) navigation_action;
462 (void) policy_decision;
464 const gchar* uri = webkit_network_request_get_uri (request);
465 if (uzbl.state.verbose)
466 printf("New window requested -> %s \n", uri);
467 new_window_load_uri(uri);
472 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
477 /* If we can display it, let's display it... */
478 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
479 webkit_web_policy_decision_use (policy_decision);
483 /* ...everything we can't displayed is downloaded */
484 webkit_web_policy_decision_download (policy_decision);
489 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
493 if (uzbl.state.selected_url != NULL) {
494 if (uzbl.state.verbose)
495 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
496 new_window_load_uri(uzbl.state.selected_url);
498 if (uzbl.state.verbose)
499 printf("New web view -> %s\n","Nothing to open, exiting");
505 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
508 if (uzbl.behave.download_handler) {
509 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
510 if (uzbl.state.verbose)
511 printf("Download -> %s\n",uri);
512 /* if urls not escaped, we may have to escape and quote uri before this call */
513 run_handler(uzbl.behave.download_handler, uri);
518 /* scroll a bar in a given direction */
520 scroll (GtkAdjustment* bar, GArray *argv) {
524 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
525 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
526 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
530 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
531 (void) page; (void) argv; (void) result;
532 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
536 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
537 (void) page; (void) argv; (void) result;
538 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
539 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
543 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
544 (void) page; (void) result;
545 scroll(uzbl.gui.bar_v, argv);
549 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
550 (void) page; (void) result;
551 scroll(uzbl.gui.bar_h, argv);
556 if (!uzbl.behave.show_status) {
557 gtk_widget_hide(uzbl.gui.mainbar);
559 gtk_widget_show(uzbl.gui.mainbar);
565 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
570 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
574 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
579 if (uzbl.behave.show_status) {
580 gtk_widget_hide(uzbl.gui.mainbar);
582 gtk_widget_show(uzbl.gui.mainbar);
584 uzbl.behave.show_status = !uzbl.behave.show_status;
589 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
593 //Set selected_url state variable
594 g_free(uzbl.state.selected_url);
595 uzbl.state.selected_url = NULL;
597 uzbl.state.selected_url = g_strdup(link);
603 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
607 if (uzbl.gui.main_title)
608 g_free (uzbl.gui.main_title);
609 uzbl.gui.main_title = g_strdup (title);
614 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
617 uzbl.gui.sbar.load_progress = progress;
622 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
626 if (uzbl.behave.load_finish_handler)
627 run_handler(uzbl.behave.load_finish_handler, "");
631 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
635 uzbl.gui.sbar.load_progress = 0;
636 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
637 if (uzbl.behave.load_start_handler)
638 run_handler(uzbl.behave.load_start_handler, "");
642 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
645 g_free (uzbl.state.uri);
646 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
647 uzbl.state.uri = g_string_free (newuri, FALSE);
648 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
649 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
652 if (uzbl.behave.load_commit_handler)
653 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
657 destroy_cb (GtkWidget* widget, gpointer data) {
665 if (uzbl.behave.history_handler) {
667 struct tm * timeinfo;
670 timeinfo = localtime ( &rawtime );
671 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
672 run_handler(uzbl.behave.history_handler, date);
677 /* VIEW funcs (little webkit wrappers) */
678 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
680 VIEWFUNC(reload_bypass_cache)
681 VIEWFUNC(stop_loading)
688 /* -- command to callback/function map for things we cannot attach to any signals */
689 static struct {char *key; CommandInfo value;} cmdlist[] =
690 { /* key function no_split */
691 { "back", {view_go_back, 0} },
692 { "forward", {view_go_forward, 0} },
693 { "scroll_vert", {scroll_vert, 0} },
694 { "scroll_horz", {scroll_horz, 0} },
695 { "scroll_begin", {scroll_begin, 0} },
696 { "scroll_end", {scroll_end, 0} },
697 { "reload", {view_reload, 0}, },
698 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
699 { "stop", {view_stop_loading, 0}, },
700 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
701 { "zoom_out", {view_zoom_out, 0}, },
702 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
703 { "uri", {load_uri, TRUE} },
704 { "js", {run_js, TRUE} },
705 { "script", {run_external_js, 0} },
706 { "toggle_status", {toggle_status_cb, 0} },
707 { "spawn", {spawn, 0} },
708 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
709 { "sh", {spawn_sh, 0} },
710 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
711 { "exit", {close_uzbl, 0} },
712 { "search", {search_forward_text, TRUE} },
713 { "search_reverse", {search_reverse_text, TRUE} },
714 { "dehilight", {dehilight, 0} },
715 { "toggle_insert_mode", {toggle_insert_mode, 0} },
716 { "set", {set_var, TRUE} },
717 //{ "get", {get_var, TRUE} },
718 { "bind", {act_bind, TRUE} },
719 { "dump_config", {act_dump_config, 0} },
720 { "keycmd", {keycmd, TRUE} },
721 { "keycmd_nl", {keycmd_nl, TRUE} },
722 { "keycmd_bs", {keycmd_bs, 0} },
723 { "chain", {chain, 0} },
724 { "print", {print, TRUE} }
731 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
733 for (i = 0; i < LENGTH(cmdlist); i++)
734 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
737 /* -- CORE FUNCTIONS -- */
740 free_action(gpointer act) {
741 Action *action = (Action*)act;
742 g_free(action->name);
744 g_free(action->param);
749 new_action(const gchar *name, const gchar *param) {
750 Action *action = g_new(Action, 1);
752 action->name = g_strdup(name);
754 action->param = g_strdup(param);
756 action->param = NULL;
762 file_exists (const char * filename) {
763 return (access(filename, F_OK) == 0);
767 set_var(WebKitWebView *page, GArray *argv, GString *result) {
768 (void) page; (void) result;
769 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
770 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
771 set_var_value(g_strstrip(split[0]), value);
777 print(WebKitWebView *page, GArray *argv, GString *result) {
778 (void) page; (void) result;
781 buf = expand(argv_idx(argv, 0), 0);
782 g_string_assign(result, buf);
787 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
788 (void) page; (void) result;
789 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
790 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
791 add_binding(g_strstrip(split[0]), value);
803 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
804 (void) page; (void) result;
806 if (argv_idx(argv, 0)) {
807 if (strcmp (argv_idx(argv, 0), "0") == 0) {
808 uzbl.behave.insert_mode = FALSE;
810 uzbl.behave.insert_mode = TRUE;
813 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
820 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
823 if (argv_idx(argv, 0)) {
824 GString* newuri = g_string_new (argv_idx(argv, 0));
825 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
826 run_js(web_view, argv, NULL);
829 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
830 g_string_prepend (newuri, "http://");
831 /* if we do handle cookies, ask our handler for them */
832 webkit_web_view_load_uri (web_view, newuri->str);
833 g_string_free (newuri, TRUE);
841 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
842 size_t argumentCount, const JSValueRef arguments[],
843 JSValueRef* exception) {
848 JSStringRef js_result_string;
849 GString *result = g_string_new("");
851 if (argumentCount >= 1) {
852 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
853 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
854 char ctl_line[arg_size];
855 JSStringGetUTF8CString(arg, ctl_line, arg_size);
857 parse_cmd_line(ctl_line, result);
859 JSStringRelease(arg);
861 js_result_string = JSStringCreateWithUTF8CString(result->str);
863 g_string_free(result, TRUE);
865 return JSValueMakeString(ctx, js_result_string);
868 static JSStaticFunction js_static_functions[] = {
869 {"run", js_run_command, kJSPropertyAttributeNone},
874 /* This function creates the class and its definition, only once */
875 if (!uzbl.js.initialized) {
876 /* it would be pretty cool to make this dynamic */
877 uzbl.js.classdef = kJSClassDefinitionEmpty;
878 uzbl.js.classdef.staticFunctions = js_static_functions;
880 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
886 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
887 WebKitWebFrame *frame;
888 JSGlobalContextRef context;
889 JSObjectRef globalobject;
890 JSStringRef var_name;
892 JSStringRef js_script;
893 JSValueRef js_result;
894 JSStringRef js_result_string;
895 size_t js_result_size;
899 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
900 context = webkit_web_frame_get_global_context(frame);
901 globalobject = JSContextGetGlobalObject(context);
903 /* uzbl javascript namespace */
904 var_name = JSStringCreateWithUTF8CString("Uzbl");
905 JSObjectSetProperty(context, globalobject, var_name,
906 JSObjectMake(context, uzbl.js.classref, NULL),
907 kJSClassAttributeNone, NULL);
909 /* evaluate the script and get return value*/
910 js_script = JSStringCreateWithUTF8CString(script);
911 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
912 if (js_result && !JSValueIsUndefined(context, js_result)) {
913 js_result_string = JSValueToStringCopy(context, js_result, NULL);
914 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
916 if (js_result_size) {
917 char js_result_utf8[js_result_size];
918 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
919 g_string_assign(result, js_result_utf8);
922 JSStringRelease(js_result_string);
926 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
928 JSStringRelease(var_name);
929 JSStringRelease(js_script);
933 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
935 if (argv_idx(argv, 0))
936 eval_js(web_view, argv_idx(argv, 0), result);
940 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
942 if (argv_idx(argv, 0)) {
943 GArray* lines = read_file_by_line (argv_idx (argv, 0));
948 while ((line = g_array_index(lines, gchar*, i))) {
950 js = g_strdup (line);
952 gchar* newjs = g_strconcat (js, line, NULL);
959 if (uzbl.state.verbose)
960 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
962 if (argv_idx (argv, 1)) {
963 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
967 eval_js (web_view, js, result);
969 g_array_free (lines, TRUE);
974 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
975 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
976 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
977 webkit_web_view_unmark_text_matches (page);
978 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
979 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
983 if (uzbl.state.searchtx) {
984 if (uzbl.state.verbose)
985 printf ("Searching: %s\n", uzbl.state.searchtx);
986 webkit_web_view_set_highlight_text_matches (page, TRUE);
987 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
992 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
994 search_text(page, argv, TRUE);
998 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1000 search_text(page, argv, FALSE);
1004 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1005 (void) argv; (void) result;
1006 webkit_web_view_set_highlight_text_matches (page, FALSE);
1011 new_window_load_uri (const gchar * uri) {
1012 GString* to_execute = g_string_new ("");
1013 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1015 for (i = 0; entries[i].long_name != NULL; i++) {
1016 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1017 gchar** str = (gchar**)entries[i].arg_data;
1019 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1023 if (uzbl.state.verbose)
1024 printf("\n%s\n", to_execute->str);
1025 g_spawn_command_line_async (to_execute->str, NULL);
1026 g_string_free (to_execute, TRUE);
1030 chain (WebKitWebView *page, GArray *argv, GString *result) {
1031 (void) page; (void) result;
1033 gchar **parts = NULL;
1035 while ((a = argv_idx(argv, i++))) {
1036 parts = g_strsplit (a, " ", 2);
1037 parse_command(parts[0], parts[1], result);
1043 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1047 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1053 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1057 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1063 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1068 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1070 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1075 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1082 /* --Statusbar functions-- */
1084 build_progressbar_ascii(int percent) {
1085 int width=uzbl.gui.sbar.progress_w;
1088 GString *bar = g_string_new("");
1090 l = (double)percent*((double)width/100.);
1091 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1093 for(i=0; i<(int)l; i++)
1094 g_string_append(bar, uzbl.gui.sbar.progress_s);
1097 g_string_append(bar, uzbl.gui.sbar.progress_u);
1099 return g_string_free(bar, FALSE);
1104 const GScannerConfig scan_config = {
1107 ) /* cset_skip_characters */,
1112 ) /* cset_identifier_first */,
1119 ) /* cset_identifier_nth */,
1120 ( "" ) /* cpair_comment_single */,
1122 TRUE /* case_sensitive */,
1124 FALSE /* skip_comment_multi */,
1125 FALSE /* skip_comment_single */,
1126 FALSE /* scan_comment_multi */,
1127 TRUE /* scan_identifier */,
1128 TRUE /* scan_identifier_1char */,
1129 FALSE /* scan_identifier_NULL */,
1130 TRUE /* scan_symbols */,
1131 FALSE /* scan_binary */,
1132 FALSE /* scan_octal */,
1133 FALSE /* scan_float */,
1134 FALSE /* scan_hex */,
1135 FALSE /* scan_hex_dollar */,
1136 FALSE /* scan_string_sq */,
1137 FALSE /* scan_string_dq */,
1138 TRUE /* numbers_2_int */,
1139 FALSE /* int_2_float */,
1140 FALSE /* identifier_2_string */,
1141 FALSE /* char_2_token */,
1142 FALSE /* symbol_2_token */,
1143 TRUE /* scope_0_fallback */,
1148 uzbl.scan = g_scanner_new(&scan_config);
1149 while(symp->symbol_name) {
1150 g_scanner_scope_add_symbol(uzbl.scan, 0,
1152 GINT_TO_POINTER(symp->symbol_token));
1158 expand_template(const char *template, gboolean escape_markup) {
1159 if(!template) return NULL;
1161 GTokenType token = G_TOKEN_NONE;
1162 GString *ret = g_string_new("");
1166 g_scanner_input_text(uzbl.scan, template, strlen(template));
1167 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1168 token = g_scanner_get_next_token(uzbl.scan);
1170 if(token == G_TOKEN_SYMBOL) {
1171 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1175 buf = uzbl.state.uri?
1176 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1177 g_string_append(ret, buf);
1181 g_string_append(ret, uzbl.state.uri?
1182 uzbl.state.uri:g_strdup(""));
1185 buf = itos(uzbl.gui.sbar.load_progress);
1186 g_string_append(ret, buf);
1189 case SYM_LOADPRGSBAR:
1190 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1191 g_string_append(ret, buf);
1196 buf = uzbl.gui.main_title?
1197 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1198 g_string_append(ret, buf);
1202 g_string_append(ret, uzbl.gui.main_title?
1203 uzbl.gui.main_title:g_strdup(""));
1205 case SYM_SELECTED_URI:
1207 buf = uzbl.state.selected_url?
1208 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1209 g_string_append(ret, buf);
1213 g_string_append(ret, uzbl.state.selected_url?
1214 uzbl.state.selected_url:g_strdup(""));
1217 buf = itos(uzbl.xwin);
1218 g_string_append(ret,
1219 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1224 buf = uzbl.state.keycmd->str?
1225 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1226 g_string_append(ret, buf);
1230 g_string_append(ret, uzbl.state.keycmd->str?
1231 uzbl.state.keycmd->str:g_strdup(""));
1234 g_string_append(ret,
1235 uzbl.behave.insert_mode?
1236 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1239 g_string_append(ret,
1240 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1242 /* useragent syms */
1244 buf = itos(WEBKIT_MAJOR_VERSION);
1245 g_string_append(ret, buf);
1249 buf = itos(WEBKIT_MINOR_VERSION);
1250 g_string_append(ret, buf);
1254 buf = itos(WEBKIT_MICRO_VERSION);
1255 g_string_append(ret, buf);
1259 g_string_append(ret, uzbl.state.unameinfo.sysname);
1262 g_string_append(ret, uzbl.state.unameinfo.nodename);
1265 g_string_append(ret, uzbl.state.unameinfo.release);
1268 g_string_append(ret, uzbl.state.unameinfo.version);
1271 g_string_append(ret, uzbl.state.unameinfo.machine);
1274 g_string_append(ret, ARCH);
1277 case SYM_DOMAINNAME:
1278 g_string_append(ret, uzbl.state.unameinfo.domainname);
1282 g_string_append(ret, COMMIT);
1288 else if(token == G_TOKEN_INT) {
1289 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1290 g_string_append(ret, buf);
1293 else if(token == G_TOKEN_IDENTIFIER) {
1294 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1296 else if(token == G_TOKEN_CHAR) {
1297 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1301 return g_string_free(ret, FALSE);
1303 /* --End Statusbar functions-- */
1306 sharg_append(GArray *a, const gchar *str) {
1307 const gchar *s = (str ? str : "");
1308 g_array_append_val(a, s);
1311 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1313 run_command (const gchar *command, const guint npre, const gchar **args,
1314 const gboolean sync, char **output_stdout) {
1315 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1318 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1319 gchar *pid = itos(getpid());
1320 gchar *xwin = itos(uzbl.xwin);
1322 sharg_append(a, command);
1323 for (i = 0; i < npre; i++) /* add n args before the default vars */
1324 sharg_append(a, args[i]);
1325 sharg_append(a, uzbl.state.config_file);
1326 sharg_append(a, pid);
1327 sharg_append(a, xwin);
1328 sharg_append(a, uzbl.comm.fifo_path);
1329 sharg_append(a, uzbl.comm.socket_path);
1330 sharg_append(a, uzbl.state.uri);
1331 sharg_append(a, uzbl.gui.main_title);
1333 for (i = npre; i < g_strv_length((gchar**)args); i++)
1334 sharg_append(a, args[i]);
1338 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1340 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1341 NULL, NULL, output_stdout, NULL, NULL, &err);
1342 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1343 NULL, NULL, NULL, &err);
1345 if (uzbl.state.verbose) {
1346 GString *s = g_string_new("spawned:");
1347 for (i = 0; i < (a->len); i++) {
1348 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1349 g_string_append_printf(s, " %s", qarg);
1352 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1353 printf("%s\n", s->str);
1354 g_string_free(s, TRUE);
1356 printf("Stdout: %s\n", *output_stdout);
1360 g_printerr("error on run_command: %s\n", err->message);
1365 g_array_free (a, TRUE);
1370 split_quoted(const gchar* src, const gboolean unquote) {
1371 /* split on unquoted space, return array of strings;
1372 remove a layer of quotes and backslashes if unquote */
1373 if (!src) return NULL;
1375 gboolean dq = FALSE;
1376 gboolean sq = FALSE;
1377 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1378 GString *s = g_string_new ("");
1382 for (p = src; *p != '\0'; p++) {
1383 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1384 else if (*p == '\\') { g_string_append_c(s, *p++);
1385 g_string_append_c(s, *p); }
1386 else if ((*p == '"') && unquote && !sq) dq = !dq;
1387 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1389 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1390 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1392 else if ((*p == ' ') && !dq && !sq) {
1393 dup = g_strdup(s->str);
1394 g_array_append_val(a, dup);
1395 g_string_truncate(s, 0);
1396 } else g_string_append_c(s, *p);
1398 dup = g_strdup(s->str);
1399 g_array_append_val(a, dup);
1400 ret = (gchar**)a->data;
1401 g_array_free (a, FALSE);
1402 g_string_free (s, TRUE);
1407 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1408 (void)web_view; (void)result;
1409 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1410 if (argv_idx(argv, 0))
1411 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1415 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1416 (void)web_view; (void)result;
1418 if (argv_idx(argv, 0))
1419 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1420 TRUE, &uzbl.comm.sync_stdout);
1424 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1425 (void)web_view; (void)result;
1426 if (!uzbl.behave.shell_cmd) {
1427 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1432 gchar *spacer = g_strdup("");
1433 g_array_insert_val(argv, 1, spacer);
1434 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1436 for (i = 1; i < g_strv_length(cmd); i++)
1437 g_array_prepend_val(argv, cmd[i]);
1439 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1445 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1446 (void)web_view; (void)result;
1447 if (!uzbl.behave.shell_cmd) {
1448 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1453 gchar *spacer = g_strdup("");
1454 g_array_insert_val(argv, 1, spacer);
1455 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1457 for (i = 1; i < g_strv_length(cmd); i++)
1458 g_array_prepend_val(argv, cmd[i]);
1460 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1461 TRUE, &uzbl.comm.sync_stdout);
1467 parse_command(const char *cmd, const char *param, GString *result) {
1470 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1472 gchar **par = split_quoted(param, TRUE);
1473 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1475 if (c->no_split) { /* don't split */
1476 sharg_append(a, param);
1478 for (i = 0; i < g_strv_length(par); i++)
1479 sharg_append(a, par[i]);
1482 if (result == NULL) {
1483 GString *result_print = g_string_new("");
1485 c->function(uzbl.gui.web_view, a, result_print);
1486 if (result_print->len)
1487 printf("%*s\n", result_print->len, result_print->str);
1489 g_string_free(result_print, TRUE);
1491 c->function(uzbl.gui.web_view, a, result);
1494 g_array_free (a, TRUE);
1497 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1504 if(*uzbl.net.proxy_url == ' '
1505 || uzbl.net.proxy_url == NULL) {
1506 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1507 (GType) SOUP_SESSION_PROXY_URI);
1510 suri = soup_uri_new(uzbl.net.proxy_url);
1511 g_object_set(G_OBJECT(uzbl.net.soup_session),
1512 SOUP_SESSION_PROXY_URI,
1514 soup_uri_free(suri);
1521 if(file_exists(uzbl.gui.icon)) {
1522 if (uzbl.gui.main_window)
1523 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1525 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1527 g_free (uzbl.gui.icon);
1532 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1533 g_array_append_val (a, uzbl.state.uri);
1534 load_uri(uzbl.gui.web_view, a, NULL);
1535 g_array_free (a, TRUE);
1539 cmd_always_insert_mode() {
1540 uzbl.behave.insert_mode =
1541 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1547 g_object_set(G_OBJECT(uzbl.net.soup_session),
1548 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1552 cmd_max_conns_host() {
1553 g_object_set(G_OBJECT(uzbl.net.soup_session),
1554 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1559 soup_session_remove_feature
1560 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1561 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1562 /*g_free(uzbl.net.soup_logger);*/
1564 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1565 soup_session_add_feature(uzbl.net.soup_session,
1566 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1569 static WebKitWebSettings*
1571 return webkit_web_view_get_settings(uzbl.gui.web_view);
1576 WebKitWebSettings *ws = view_settings();
1577 if (uzbl.behave.font_size > 0) {
1578 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1581 if (uzbl.behave.monospace_size > 0) {
1582 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1583 uzbl.behave.monospace_size, NULL);
1585 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1586 uzbl.behave.font_size, NULL);
1592 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1596 cmd_disable_plugins() {
1597 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1598 !uzbl.behave.disable_plugins, NULL);
1602 cmd_disable_scripts() {
1603 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1604 !uzbl.behave.disable_scripts, NULL);
1608 cmd_minimum_font_size() {
1609 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1610 uzbl.behave.minimum_font_size, NULL);
1613 cmd_autoload_img() {
1614 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1615 uzbl.behave.autoload_img, NULL);
1620 cmd_autoshrink_img() {
1621 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1622 uzbl.behave.autoshrink_img, NULL);
1627 cmd_enable_spellcheck() {
1628 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1629 uzbl.behave.enable_spellcheck, NULL);
1633 cmd_enable_private() {
1634 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1635 uzbl.behave.enable_private, NULL);
1640 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1641 uzbl.behave.print_bg, NULL);
1646 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1647 uzbl.behave.style_uri, NULL);
1651 cmd_resizable_txt() {
1652 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1653 uzbl.behave.resizable_txt, NULL);
1657 cmd_default_encoding() {
1658 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1659 uzbl.behave.default_encoding, NULL);
1663 cmd_enforce_96dpi() {
1664 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1665 uzbl.behave.enforce_96dpi, NULL);
1669 cmd_caret_browsing() {
1670 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1671 uzbl.behave.caret_browsing, NULL);
1675 cmd_cookie_handler() {
1676 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1677 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1678 if ((g_strcmp0(split[0], "sh") == 0) ||
1679 (g_strcmp0(split[0], "spawn") == 0)) {
1680 g_free (uzbl.behave.cookie_handler);
1681 uzbl.behave.cookie_handler =
1682 g_strdup_printf("sync_%s %s", split[0], split[1]);
1689 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1694 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1699 if(uzbl.behave.inject_html) {
1700 webkit_web_view_load_html_string (uzbl.gui.web_view,
1701 uzbl.behave.inject_html, NULL);
1710 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1711 uzbl.behave.modmask = 0;
1713 if(uzbl.behave.modkey)
1714 g_free(uzbl.behave.modkey);
1715 uzbl.behave.modkey = buf;
1717 for (i = 0; modkeys[i].key != NULL; i++) {
1718 if (g_strrstr(buf, modkeys[i].key))
1719 uzbl.behave.modmask |= modkeys[i].mask;
1725 if (*uzbl.net.useragent == ' ') {
1726 g_free (uzbl.net.useragent);
1727 uzbl.net.useragent = NULL;
1729 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1731 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1732 g_free(uzbl.net.useragent);
1733 uzbl.net.useragent = ua;
1739 gtk_widget_ref(uzbl.gui.scrolled_win);
1740 gtk_widget_ref(uzbl.gui.mainbar);
1741 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1742 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1744 if(uzbl.behave.status_top) {
1745 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1746 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1749 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1750 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1752 gtk_widget_unref(uzbl.gui.scrolled_win);
1753 gtk_widget_unref(uzbl.gui.mainbar);
1754 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1759 set_var_value(gchar *name, gchar *val) {
1760 uzbl_cmdprop *c = NULL;
1764 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1765 /* check for the variable type */
1766 if (c->type == TYPE_STR) {
1767 buf = expand(val, 0);
1770 } else if(c->type == TYPE_INT) {
1771 int *ip = (int *)c->ptr;
1772 buf = expand(val, 0);
1773 *ip = (int)strtoul(buf, &endp, 10);
1775 } else if (c->type == TYPE_FLOAT) {
1776 float *fp = (float *)c->ptr;
1777 buf = expand(val, 0);
1778 *fp = strtod(buf, &endp);
1782 /* invoke a command specific function */
1783 if(c->func) c->func();
1790 Behaviour *b = &uzbl.behave;
1792 if(b->html_buffer->str) {
1793 webkit_web_view_load_html_string (uzbl.gui.web_view,
1794 b->html_buffer->str, b->base_url);
1795 g_string_free(b->html_buffer, TRUE);
1796 b->html_buffer = g_string_new("");
1800 enum {M_CMD, M_HTML};
1802 parse_cmd_line(const char *ctl_line, GString *result) {
1803 Behaviour *b = &uzbl.behave;
1806 if(b->mode == M_HTML) {
1807 len = strlen(b->html_endmarker);
1808 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1809 if(len == strlen(ctl_line)-1 &&
1810 !strncmp(b->html_endmarker, ctl_line, len)) {
1812 set_var_value("mode", "0");
1817 set_timeout(b->html_timeout);
1818 g_string_append(b->html_buffer, ctl_line);
1821 else if((ctl_line[0] == '#') /* Comments */
1822 || (ctl_line[0] == ' ')
1823 || (ctl_line[0] == '\n'))
1824 ; /* ignore these lines */
1825 else { /* parse a command */
1827 gchar **tokens = NULL;
1828 len = strlen(ctl_line);
1830 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1831 ctlstrip = g_strndup(ctl_line, len - 1);
1832 else ctlstrip = g_strdup(ctl_line);
1834 tokens = g_strsplit(ctlstrip, " ", 2);
1835 parse_command(tokens[0], tokens[1], result);
1842 build_stream_name(int type, const gchar* dir) {
1843 char *xwin_str = NULL;
1844 State *s = &uzbl.state;
1847 xwin_str = itos((int)uzbl.xwin);
1849 str = g_strdup_printf
1850 ("%s/uzbl_fifo_%s", dir,
1851 s->instance_name ? s->instance_name : xwin_str);
1852 } else if (type == SOCKET) {
1853 str = g_strdup_printf
1854 ("%s/uzbl_socket_%s", dir,
1855 s->instance_name ? s->instance_name : xwin_str );
1862 control_fifo(GIOChannel *gio, GIOCondition condition) {
1863 if (uzbl.state.verbose)
1864 printf("triggered\n");
1869 if (condition & G_IO_HUP)
1870 g_error ("Fifo: Read end of pipe died!\n");
1873 g_error ("Fifo: GIOChannel broke\n");
1875 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1876 if (ret == G_IO_STATUS_ERROR) {
1877 g_error ("Fifo: Error reading: %s\n", err->message);
1881 parse_cmd_line(ctl_line, NULL);
1888 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1889 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1890 if (unlink(uzbl.comm.fifo_path) == -1)
1891 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1892 g_free(uzbl.comm.fifo_path);
1893 uzbl.comm.fifo_path = NULL;
1896 if (*dir == ' ') { /* space unsets the variable */
1901 GIOChannel *chan = NULL;
1902 GError *error = NULL;
1903 gchar *path = build_stream_name(FIFO, dir);
1905 if (!file_exists(path)) {
1906 if (mkfifo (path, 0666) == 0) {
1907 // 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.
1908 chan = g_io_channel_new_file(path, "r+", &error);
1910 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1911 if (uzbl.state.verbose)
1912 printf ("init_fifo: created successfully as %s\n", path);
1913 uzbl.comm.fifo_path = path;
1915 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1916 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1917 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1918 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1920 /* if we got this far, there was an error; cleanup */
1921 if (error) g_error_free (error);
1928 control_stdin(GIOChannel *gio, GIOCondition condition) {
1930 gchar *ctl_line = NULL;
1933 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1934 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1937 parse_cmd_line(ctl_line, NULL);
1945 GIOChannel *chan = NULL;
1946 GError *error = NULL;
1948 chan = g_io_channel_unix_new(fileno(stdin));
1950 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1951 g_error ("Stdin: could not add watch\n");
1953 if (uzbl.state.verbose)
1954 printf ("Stdin: watch added successfully\n");
1957 g_error ("Stdin: Error while opening: %s\n", error->message);
1959 if (error) g_error_free (error);
1963 control_socket(GIOChannel *chan) {
1964 struct sockaddr_un remote;
1965 unsigned int t = sizeof(remote);
1967 GIOChannel *clientchan;
1969 clientsock = accept (g_io_channel_unix_get_fd(chan),
1970 (struct sockaddr *) &remote, &t);
1972 if ((clientchan = g_io_channel_unix_new(clientsock))) {
1973 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
1974 (GIOFunc) control_client_socket, clientchan);
1981 control_client_socket(GIOChannel *clientchan) {
1983 GString *result = g_string_new("");
1984 GError *error = NULL;
1988 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
1989 if (ret == G_IO_STATUS_ERROR) {
1990 g_warning ("Error reading: %s\n", error->message);
1991 g_io_channel_shutdown(clientchan, TRUE, &error);
1993 } else if (ret == G_IO_STATUS_EOF) {
1994 /* shutdown and remove channel watch from main loop */
1995 g_io_channel_shutdown(clientchan, TRUE, &error);
2000 parse_cmd_line (ctl_line, result);
2001 g_string_append_c(result, '\n');
2002 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2004 if (ret == G_IO_STATUS_ERROR) {
2005 g_warning ("Error writing: %s", error->message);
2007 g_io_channel_flush(clientchan, &error);
2010 if (error) g_error_free (error);
2011 g_string_free(result, TRUE);
2017 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2018 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2019 if (unlink(uzbl.comm.socket_path) == -1)
2020 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2021 g_free(uzbl.comm.socket_path);
2022 uzbl.comm.socket_path = NULL;
2030 GIOChannel *chan = NULL;
2032 struct sockaddr_un local;
2033 gchar *path = build_stream_name(SOCKET, dir);
2035 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2037 local.sun_family = AF_UNIX;
2038 strcpy (local.sun_path, path);
2039 unlink (local.sun_path);
2041 len = strlen (local.sun_path) + sizeof (local.sun_family);
2042 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2043 if (uzbl.state.verbose)
2044 printf ("init_socket: opened in %s\n", path);
2047 if( (chan = g_io_channel_unix_new(sock)) ) {
2048 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2049 uzbl.comm.socket_path = path;
2052 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2054 /* if we got this far, there was an error; cleanup */
2061 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2062 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2064 // this function may be called very early when the templates are not set (yet), hence the checks
2066 update_title (void) {
2067 Behaviour *b = &uzbl.behave;
2070 if (b->show_status) {
2071 if (b->title_format_short) {
2072 parsed = expand_template(b->title_format_short, FALSE);
2073 if (uzbl.gui.main_window)
2074 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2077 if (b->status_format) {
2078 parsed = expand_template(b->status_format, TRUE);
2079 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2082 if (b->status_background) {
2084 gdk_color_parse (b->status_background, &color);
2085 //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)
2086 if (uzbl.gui.main_window)
2087 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2090 if (b->title_format_long) {
2091 parsed = expand_template(b->title_format_long, FALSE);
2092 if (uzbl.gui.main_window)
2093 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2100 key_press_cb (GtkWidget* window, GdkEventKey* event)
2102 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2106 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
2107 || 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)
2110 /* turn off insert mode (if always_insert_mode is not used) */
2111 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2112 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2117 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
2120 if (event->keyval == GDK_Escape) {
2121 g_string_truncate(uzbl.state.keycmd, 0);
2123 dehilight(uzbl.gui.web_view, NULL, NULL);
2127 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2128 if (event->keyval == GDK_Insert) {
2130 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2131 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2133 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2136 g_string_append (uzbl.state.keycmd, str);
2143 if (event->keyval == GDK_BackSpace)
2144 keycmd_bs(NULL, NULL, NULL);
2146 gboolean key_ret = FALSE;
2147 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2149 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2151 run_keycmd(key_ret);
2153 if (key_ret) return (!uzbl.behave.insert_mode);
2158 run_keycmd(const gboolean key_ret) {
2159 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2161 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2162 g_string_truncate(uzbl.state.keycmd, 0);
2163 parse_command(act->name, act->param, NULL);
2167 /* try if it's an incremental keycmd or one that takes args, and run it */
2168 GString* short_keys = g_string_new ("");
2169 GString* short_keys_inc = g_string_new ("");
2171 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2172 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2173 g_string_assign(short_keys_inc, short_keys->str);
2174 g_string_append_c(short_keys, '_');
2175 g_string_append_c(short_keys_inc, '*');
2177 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2178 /* run normal cmds only if return was pressed */
2179 exec_paramcmd(act, i);
2180 g_string_truncate(uzbl.state.keycmd, 0);
2182 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2183 if (key_ret) /* just quit the incremental command on return */
2184 g_string_truncate(uzbl.state.keycmd, 0);
2185 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2189 g_string_truncate(short_keys, short_keys->len - 1);
2191 g_string_free (short_keys, TRUE);
2192 g_string_free (short_keys_inc, TRUE);
2196 exec_paramcmd(const Action *act, const guint i) {
2197 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2198 GString *actionname = g_string_new ("");
2199 GString *actionparam = g_string_new ("");
2200 g_string_erase (parampart, 0, i+1);
2202 g_string_printf (actionname, act->name, parampart->str);
2204 g_string_printf (actionparam, act->param, parampart->str);
2205 parse_command(actionname->str, actionparam->str, NULL);
2206 g_string_free(actionname, TRUE);
2207 g_string_free(actionparam, TRUE);
2208 g_string_free(parampart, TRUE);
2216 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2217 //main_window_ref = g_object_ref(scrolled_window);
2218 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
2220 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2221 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2223 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
2224 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2225 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2226 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2227 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2228 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2229 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2230 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2231 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2232 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2233 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2235 return scrolled_window;
2242 g->mainbar = gtk_hbox_new (FALSE, 0);
2244 /* keep a reference to the bar so we can re-pack it at runtime*/
2245 //sbar_ref = g_object_ref(g->mainbar);
2247 g->mainbar_label = gtk_label_new ("");
2248 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2249 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2250 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2251 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2252 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2253 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2258 GtkWidget* create_window () {
2259 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2260 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2261 gtk_widget_set_name (window, "Uzbl browser");
2262 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2263 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2269 GtkPlug* create_plug () {
2270 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2271 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2272 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2279 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2281 If actname is one that calls an external command, this function will inject
2282 newargs in front of the user-provided args in that command line. They will
2283 come become after the body of the script (in sh) or after the name of
2284 the command to execute (in spawn).
2285 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2286 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2288 The return value consist of two strings: the action (sh, ...) and its args.
2290 If act is not one that calls an external command, then the given action merely
2293 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2294 gchar *actdup = g_strdup(actname);
2295 g_array_append_val(rets, actdup);
2297 if ((g_strcmp0(actname, "spawn") == 0) ||
2298 (g_strcmp0(actname, "sh") == 0) ||
2299 (g_strcmp0(actname, "sync_spawn") == 0) ||
2300 (g_strcmp0(actname, "sync_sh") == 0)) {
2302 GString *a = g_string_new("");
2303 gchar **spawnparts = split_quoted(origargs, FALSE);
2304 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2305 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2307 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2308 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2310 g_array_append_val(rets, a->str);
2311 g_string_free(a, FALSE);
2312 g_strfreev(spawnparts);
2314 gchar *origdup = g_strdup(origargs);
2315 g_array_append_val(rets, origdup);
2317 return (gchar**)g_array_free(rets, FALSE);
2321 run_handler (const gchar *act, const gchar *args) {
2322 /* Consider this code a temporary hack to make the handlers usable.
2323 In practice, all this splicing, injection, and reconstruction is
2324 inefficient, annoying and hard to manage. Potential pitfalls arise
2325 when the handler specific args 1) are not quoted (the handler
2326 callbacks should take care of this) 2) are quoted but interfere
2327 with the users' own quotation. A more ideal solution is
2328 to refactor parse_command so that it doesn't just take a string
2329 and execute it; rather than that, we should have a function which
2330 returns the argument vector parsed from the string. This vector
2331 could be modified (e.g. insert additional args into it) before
2332 passing it to the next function that actually executes it. Though
2333 it still isn't perfect for chain actions.. will reconsider & re-
2334 factor when I have the time. -duc */
2336 char **parts = g_strsplit(act, " ", 2);
2338 if (g_strcmp0(parts[0], "chain") == 0) {
2339 GString *newargs = g_string_new("");
2340 gchar **chainparts = split_quoted(parts[1], FALSE);
2342 /* for every argument in the chain, inject the handler args
2343 and make sure the new parts are wrapped in quotes */
2344 gchar **cp = chainparts;
2346 gchar *quotless = NULL;
2347 gchar **spliced_quotless = NULL; // sigh -_-;
2348 gchar **inpart = NULL;
2351 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2353 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2354 } else quotless = g_strdup(*cp);
2356 spliced_quotless = g_strsplit(quotless, " ", 2);
2357 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2358 g_strfreev(spliced_quotless);
2360 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2366 parse_command(parts[0], &(newargs->str[1]), NULL);
2367 g_string_free(newargs, TRUE);
2368 g_strfreev(chainparts);
2371 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2372 parse_command(inparts[0], inparts[1], NULL);
2380 add_binding (const gchar *key, const gchar *act) {
2381 char **parts = g_strsplit(act, " ", 2);
2388 if (uzbl.state.verbose)
2389 printf ("Binding %-10s : %s\n", key, act);
2390 action = new_action(parts[0], parts[1]);
2392 if (g_hash_table_remove (uzbl.bindings, key))
2393 g_warning ("Overwriting existing binding for \"%s\"", key);
2394 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2399 get_xdg_var (XDG_Var xdg) {
2400 const gchar* actual_value = getenv (xdg.environmental);
2401 const gchar* home = getenv ("HOME");
2402 gchar* return_value;
2404 if (! actual_value || strcmp (actual_value, "") == 0) {
2405 if (xdg.default_value) {
2406 return_value = str_replace ("~", home, xdg.default_value);
2408 return_value = NULL;
2411 return_value = str_replace("~", home, actual_value);
2414 return return_value;
2418 find_xdg_file (int xdg_type, char* filename) {
2419 /* xdg_type = 0 => config
2420 xdg_type = 1 => data
2421 xdg_type = 2 => cache*/
2423 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2424 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2427 gchar* temporary_string;
2431 if (! file_exists (temporary_file) && xdg_type != 2) {
2432 buf = get_xdg_var (XDG[3 + xdg_type]);
2433 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2436 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2437 g_free (temporary_file);
2438 temporary_file = g_strconcat (temporary_string, filename, NULL);
2442 //g_free (temporary_string); - segfaults.
2444 if (file_exists (temporary_file)) {
2445 return temporary_file;
2452 State *s = &uzbl.state;
2453 Network *n = &uzbl.net;
2455 for (i = 0; default_config[i].command != NULL; i++) {
2456 parse_cmd_line(default_config[i].command, NULL);
2459 if (g_strcmp0(s->config_file, "-") == 0) {
2460 s->config_file = NULL;
2464 if (!s->config_file) {
2465 s->config_file = find_xdg_file (0, "/uzbl/config");
2468 if (s->config_file) {
2469 GArray* lines = read_file_by_line (s->config_file);
2473 while ((line = g_array_index(lines, gchar*, i))) {
2474 parse_cmd_line (line, NULL);
2478 g_array_free (lines, TRUE);
2480 if (uzbl.state.verbose)
2481 printf ("No configuration file loaded.\n");
2484 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2487 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2490 if (!uzbl.behave.cookie_handler)
2493 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2494 GString *s = g_string_new ("");
2495 SoupURI * soup_uri = soup_message_get_uri(msg);
2496 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2497 run_handler(uzbl.behave.cookie_handler, s->str);
2499 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2500 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2501 if ( p != NULL ) *p = '\0';
2502 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2504 if (uzbl.comm.sync_stdout)
2505 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2507 g_string_free(s, TRUE);
2511 save_cookies (SoupMessage *msg, gpointer user_data){
2515 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2516 cookie = soup_cookie_to_set_cookie_header(ck->data);
2517 SoupURI * soup_uri = soup_message_get_uri(msg);
2518 GString *s = g_string_new ("");
2519 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2520 run_handler(uzbl.behave.cookie_handler, s->str);
2522 g_string_free(s, TRUE);
2527 /* --- WEBINSPECTOR --- */
2529 hide_window_cb(GtkWidget *widget, gpointer data) {
2532 gtk_widget_hide(widget);
2535 static WebKitWebView*
2536 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2539 (void) web_inspector;
2540 GtkWidget* scrolled_window;
2541 GtkWidget* new_web_view;
2544 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2545 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2546 G_CALLBACK(hide_window_cb), NULL);
2548 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2549 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2550 gtk_widget_show(g->inspector_window);
2552 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2553 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2554 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2555 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2556 gtk_widget_show(scrolled_window);
2558 new_web_view = webkit_web_view_new();
2559 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2561 return WEBKIT_WEB_VIEW(new_web_view);
2565 inspector_show_window_cb (WebKitWebInspector* inspector){
2567 gtk_widget_show(uzbl.gui.inspector_window);
2571 /* TODO: Add variables and code to make use of these functions */
2573 inspector_close_window_cb (WebKitWebInspector* inspector){
2579 inspector_attach_window_cb (WebKitWebInspector* inspector){
2585 inspector_detach_window_cb (WebKitWebInspector* inspector){
2591 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2597 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2603 set_up_inspector() {
2605 WebKitWebSettings *settings = view_settings();
2606 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2608 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2609 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2610 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2611 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2612 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2613 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2614 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2616 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2620 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2622 uzbl_cmdprop *c = v;
2627 if(c->type == TYPE_STR)
2628 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2629 else if(c->type == TYPE_INT)
2630 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2634 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2638 printf("bind %s = %s %s\n", (char *)k ,
2639 (char *)a->name, a->param?(char *)a->param:"");
2644 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2645 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2650 main (int argc, char* argv[]) {
2651 gtk_init (&argc, &argv);
2652 if (!g_thread_supported ())
2653 g_thread_init (NULL);
2654 uzbl.state.executable_path = g_strdup(argv[0]);
2655 uzbl.state.selected_url = NULL;
2656 uzbl.state.searchtx = NULL;
2658 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2659 g_option_context_add_main_entries (context, entries, NULL);
2660 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2661 g_option_context_parse (context, &argc, &argv, NULL);
2662 g_option_context_free(context);
2664 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2665 gboolean verbose_override = uzbl.state.verbose;
2667 /* initialize hash table */
2668 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2670 uzbl.net.soup_session = webkit_get_default_session();
2671 uzbl.state.keycmd = g_string_new("");
2673 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2674 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2675 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2676 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2677 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2678 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2681 if(uname(&uzbl.state.unameinfo) == -1)
2682 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2684 uzbl.gui.sbar.progress_s = g_strdup("=");
2685 uzbl.gui.sbar.progress_u = g_strdup("ยท");
2686 uzbl.gui.sbar.progress_w = 10;
2688 /* HTML mode defaults*/
2689 uzbl.behave.html_buffer = g_string_new("");
2690 uzbl.behave.html_endmarker = g_strdup(".");
2691 uzbl.behave.html_timeout = 60;
2692 uzbl.behave.base_url = g_strdup("http://invalid");
2694 /* default mode indicators */
2695 uzbl.behave.insert_indicator = g_strdup("I");
2696 uzbl.behave.cmd_indicator = g_strdup("C");
2700 make_var_to_name_hash();
2702 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2704 uzbl.gui.scrolled_win = create_browser();
2707 /* initial packing */
2708 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2709 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2711 if (uzbl.state.socket_id) {
2712 uzbl.gui.plug = create_plug ();
2713 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2714 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2716 uzbl.gui.main_window = create_window ();
2717 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2718 gtk_widget_show_all (uzbl.gui.main_window);
2719 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2722 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2724 if (uzbl.state.verbose) {
2725 printf("Uzbl start location: %s\n", argv[0]);
2726 if (uzbl.state.socket_id)
2727 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2729 printf("window_id %i\n",(int) uzbl.xwin);
2730 printf("pid %i\n", getpid ());
2731 printf("name: %s\n", uzbl.state.instance_name);
2734 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2735 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2736 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2737 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2738 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2742 if (!uzbl.behave.show_status)
2743 gtk_widget_hide(uzbl.gui.mainbar);
2750 if (verbose_override > uzbl.state.verbose)
2751 uzbl.state.verbose = verbose_override;
2754 set_var_value("uri", uri_override);
2755 g_free(uri_override);
2756 } else if (uzbl.state.uri)
2757 cmd_load_uri(uzbl.gui.web_view, NULL);
2762 return EXIT_SUCCESS;
2765 /* vi: set et ts=4: */