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 { "geometry", 'g', 0, G_OPTION_ARG_STRING, &uzbl.gui.geometry,
79 "Set window geometry (format: WIDTHxHEIGHT+-X+-Y)", "GEOMETRY" },
80 { NULL, 0, 0, 0, NULL, NULL, NULL }
83 /* associate command names to their properties */
84 typedef const struct {
91 enum {TYPE_INT, TYPE_STR, TYPE_FLOAT};
93 /* an abbreviation to help keep the table's width humane */
94 #define PTR(var, t, d, fun) { .ptr = (void*)&(var), .type = TYPE_##t, .dump = d, .func = fun }
99 } var_name_to_ptr[] = {
100 /* variable name pointer to variable in code type dump callback function */
101 /* --------------------------------------------------------------------------------------- */
102 { "uri", PTR(uzbl.state.uri, STR, 1, cmd_load_uri)},
103 { "geometry", PTR(uzbl.gui.geometry, STR, 1, cmd_set_geometry)},
104 { "verbose", PTR(uzbl.state.verbose, INT, 1, NULL)},
105 { "mode", PTR(uzbl.behave.mode, INT, 0, NULL)},
106 { "inject_html", PTR(uzbl.behave.inject_html, STR, 0, cmd_inject_html)},
107 { "base_url", PTR(uzbl.behave.base_url, STR, 1, NULL)},
108 { "html_endmarker", PTR(uzbl.behave.html_endmarker, STR, 1, NULL)},
109 { "html_mode_timeout", PTR(uzbl.behave.html_timeout, INT, 1, NULL)},
110 { "status_message", PTR(uzbl.gui.sbar.msg, STR, 1, update_title)},
111 { "show_status", PTR(uzbl.behave.show_status, INT, 1, cmd_set_status)},
112 { "status_top", PTR(uzbl.behave.status_top, INT, 1, move_statusbar)},
113 { "status_format", PTR(uzbl.behave.status_format, STR, 1, update_title)},
114 { "status_pbar_done", PTR(uzbl.gui.sbar.progress_s, STR, 1, update_title)},
115 { "status_pbar_pending", PTR(uzbl.gui.sbar.progress_u, STR, 1, update_title)},
116 { "status_pbar_width", PTR(uzbl.gui.sbar.progress_w, INT, 1, update_title)},
117 { "status_background", PTR(uzbl.behave.status_background, STR, 1, update_title)},
118 { "insert_indicator", PTR(uzbl.behave.insert_indicator, STR, 1, update_title)},
119 { "command_indicator", PTR(uzbl.behave.cmd_indicator, STR, 1, update_title)},
120 { "title_format_long", PTR(uzbl.behave.title_format_long, STR, 1, update_title)},
121 { "title_format_short", PTR(uzbl.behave.title_format_short, STR, 1, update_title)},
122 { "icon", PTR(uzbl.gui.icon, STR, 1, set_icon)},
123 { "insert_mode", PTR(uzbl.behave.insert_mode, INT, 1, NULL)},
124 { "always_insert_mode", PTR(uzbl.behave.always_insert_mode, INT, 1, cmd_always_insert_mode)},
125 { "reset_command_mode", PTR(uzbl.behave.reset_command_mode, INT, 1, NULL)},
126 { "modkey", PTR(uzbl.behave.modkey, STR, 1, cmd_modkey)},
127 { "load_finish_handler", PTR(uzbl.behave.load_finish_handler, STR, 1, NULL)},
128 { "load_start_handler", PTR(uzbl.behave.load_start_handler, STR, 1, NULL)},
129 { "load_commit_handler", PTR(uzbl.behave.load_commit_handler, STR, 1, NULL)},
130 { "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
131 { "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
132 { "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
133 { "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
134 { "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
135 { "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
136 { "shell_cmd", PTR(uzbl.behave.shell_cmd, STR, 1, NULL)},
137 { "proxy_url", PTR(uzbl.net.proxy_url, STR, 1, set_proxy_url)},
138 { "max_conns", PTR(uzbl.net.max_conns, INT, 1, cmd_max_conns)},
139 { "max_conns_host", PTR(uzbl.net.max_conns_host, INT, 1, cmd_max_conns_host)},
140 { "useragent", PTR(uzbl.net.useragent, STR, 1, cmd_useragent)},
141 /* exported WebKitWebSettings properties */
142 { "zoom_level", PTR(uzbl.behave.zoom_level, FLOAT,1, cmd_zoom_level)},
143 { "font_size", PTR(uzbl.behave.font_size, INT, 1, cmd_font_size)},
144 { "monospace_size", PTR(uzbl.behave.monospace_size, INT, 1, cmd_font_size)},
145 { "minimum_font_size", PTR(uzbl.behave.minimum_font_size, INT, 1, cmd_minimum_font_size)},
146 { "disable_plugins", PTR(uzbl.behave.disable_plugins, INT, 1, cmd_disable_plugins)},
147 { "disable_scripts", PTR(uzbl.behave.disable_scripts, INT, 1, cmd_disable_scripts)},
148 { "autoload_images", PTR(uzbl.behave.autoload_img, INT, 1, cmd_autoload_img)},
149 { "autoshrink_images", PTR(uzbl.behave.autoshrink_img, INT, 1, cmd_autoshrink_img)},
150 { "enable_spellcheck", PTR(uzbl.behave.enable_spellcheck, INT, 1, cmd_enable_spellcheck)},
151 { "enable_private", PTR(uzbl.behave.enable_private, INT, 1, cmd_enable_private)},
152 { "print_backgrounds", PTR(uzbl.behave.print_bg, INT, 1, cmd_print_bg)},
153 { "stylesheet_uri", PTR(uzbl.behave.style_uri, STR, 1, cmd_style_uri)},
154 { "resizable_text_areas",PTR(uzbl.behave.resizable_txt, INT, 1, cmd_resizable_txt)},
155 { "default_encoding", PTR(uzbl.behave.default_encoding, STR, 1, cmd_default_encoding)},
156 { "enforce_96_dpi", PTR(uzbl.behave.enforce_96dpi, INT, 1, cmd_enforce_96dpi)},
157 { "caret_browsing", PTR(uzbl.behave.caret_browsing, INT, 1, cmd_caret_browsing)},
159 { NULL, {.ptr = NULL, .type = TYPE_INT, .dump = 0, .func = NULL}}
160 }, *n2v_p = var_name_to_ptr;
166 { "SHIFT", GDK_SHIFT_MASK }, // shift
167 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
168 { "CONTROL", GDK_CONTROL_MASK }, // control
169 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
170 { "MOD2", GDK_MOD2_MASK }, // 5th mod
171 { "MOD3", GDK_MOD3_MASK }, // 6th mod
172 { "MOD4", GDK_MOD4_MASK }, // 7th mod
173 { "MOD5", GDK_MOD5_MASK }, // 8th mod
174 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
175 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
176 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
177 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
178 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
179 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
180 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
181 { "META", GDK_META_MASK }, // meta (since 2.10)
186 /* construct a hash from the var_name_to_ptr array for quick access */
188 make_var_to_name_hash() {
189 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
191 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
196 /* --- UTILITY FUNCTIONS --- */
198 enum {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS};
200 get_exp_type(gchar *s) {
204 else if(*(s+1) == '{')
205 return EXP_BRACED_VAR;
206 else if(*(s+1) == '<')
209 return EXP_SIMPLE_VAR;
215 * recurse == 1: don't expand '@(command)@'
216 * recurse == 2: don't expand '@<java script>@'
219 expand(char *s, guint recurse) {
223 char *end_simple_var = "^°!\"§$%&/()=?'`'+~*'#-.:,;@<>| \\{}[]¹²³¼½";
228 gchar *cmd_stdout = NULL;
230 GString *buf = g_string_new("");
231 GString *js_ret = g_string_new("");
236 g_string_append_c(buf, *++s);
241 etype = get_exp_type(s);
246 if( (vend = strpbrk(s, end_simple_var)) ||
247 (vend = strchr(s, '\0')) ) {
248 strncpy(ret, s, vend-s);
254 if( (vend = strchr(s, upto)) ||
255 (vend = strchr(s, '\0')) ) {
256 strncpy(ret, s, vend-s);
262 strcpy(str_end, ")@");
264 if( (vend = strstr(s, str_end)) ||
265 (vend = strchr(s, '\0')) ) {
266 strncpy(ret, s, vend-s);
272 strcpy(str_end, ">@");
274 if( (vend = strstr(s, str_end)) ||
275 (vend = strchr(s, '\0')) ) {
276 strncpy(ret, s, vend-s);
282 if(etype == EXP_SIMPLE_VAR ||
283 etype == EXP_BRACED_VAR) {
284 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
285 if(c->type == TYPE_STR)
286 g_string_append(buf, (gchar *)*c->ptr);
287 else if(c->type == TYPE_INT) {
288 char *b = itos((int)*c->ptr);
289 g_string_append(buf, b);
293 if(etype == EXP_SIMPLE_VAR)
298 else if(recurse != 1 &&
300 mycmd = expand(ret, 1);
301 g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
305 g_printerr("error on running command: %s\n", err->message);
308 else if (*cmd_stdout) {
309 g_string_append(buf, cmd_stdout);
314 else if(recurse != 2 &&
316 mycmd = expand(ret, 2);
317 eval_js(uzbl.gui.web_view, mycmd, js_ret);
321 g_string_append(buf, js_ret->str);
322 g_string_free(js_ret, TRUE);
323 js_ret = g_string_new("");
330 g_string_append_c(buf, *s);
335 g_string_free(js_ret, TRUE);
336 return g_string_free(buf, FALSE);
343 snprintf(tmp, sizeof(tmp), "%i", val);
344 return g_strdup(tmp);
348 strfree(gchar *str) { g_free(str); return NULL; } // for freeing & setting to null in one go
351 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
354 str_replace (const char* search, const char* replace, const char* string) {
358 buf = g_strsplit (string, search, -1);
359 ret = g_strjoinv (replace, buf);
360 g_strfreev(buf); // somebody said this segfaults
366 read_file_by_line (gchar *path) {
367 GIOChannel *chan = NULL;
368 gchar *readbuf = NULL;
370 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
373 chan = g_io_channel_new_file(path, "r", NULL);
376 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
377 const gchar* val = g_strdup (readbuf);
378 g_array_append_val (lines, val);
383 g_io_channel_unref (chan);
385 fprintf(stderr, "File '%s' not be read.\n", path);
392 gchar* parseenv (char* string) {
393 extern char** environ;
394 gchar* tmpstr = NULL;
398 while (environ[i] != NULL) {
399 gchar** env = g_strsplit (environ[i], "=", 2);
400 gchar* envname = g_strconcat ("$", env[0], NULL);
402 if (g_strrstr (string, envname) != NULL) {
403 tmpstr = g_strdup(string);
405 string = str_replace(envname, env[1], tmpstr);
410 g_strfreev (env); // somebody said this breaks uzbl
418 setup_signal(int signr, sigfunc *shandler) {
419 struct sigaction nh, oh;
421 nh.sa_handler = shandler;
422 sigemptyset(&nh.sa_mask);
425 if(sigaction(signr, &nh, &oh) < 0)
433 if (uzbl.behave.fifo_dir)
434 unlink (uzbl.comm.fifo_path);
435 if (uzbl.behave.socket_dir)
436 unlink (uzbl.comm.socket_path);
438 g_free(uzbl.state.executable_path);
439 g_string_free(uzbl.state.keycmd, TRUE);
440 g_hash_table_destroy(uzbl.bindings);
441 g_hash_table_destroy(uzbl.behave.commands);
444 /* used for html_mode_timeout
445 * be sure to extend this function to use
446 * more timers if needed in other places
449 set_timeout(int seconds) {
451 memset(&t, 0, sizeof t);
453 t.it_value.tv_sec = seconds;
454 t.it_value.tv_usec = 0;
455 setitimer(ITIMER_REAL, &t, NULL);
458 /* --- SIGNAL HANDLER --- */
461 catch_sigterm(int s) {
467 catch_sigint(int s) {
477 set_var_value("mode", "0");
482 /* --- CALLBACKS --- */
485 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
488 (void) navigation_action;
489 (void) policy_decision;
491 const gchar* uri = webkit_network_request_get_uri (request);
492 if (uzbl.state.verbose)
493 printf("New window requested -> %s \n", uri);
494 new_window_load_uri(uri);
499 mime_policy_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, gchar *mime_type, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
504 /* If we can display it, let's display it... */
505 if (webkit_web_view_can_show_mime_type (web_view, mime_type)) {
506 webkit_web_policy_decision_use (policy_decision);
510 /* ...everything we can't displayed is downloaded */
511 webkit_web_policy_decision_download (policy_decision);
516 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
520 if (uzbl.state.selected_url != NULL) {
521 if (uzbl.state.verbose)
522 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
523 new_window_load_uri(uzbl.state.selected_url);
525 if (uzbl.state.verbose)
526 printf("New web view -> %s\n","Nothing to open, exiting");
532 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
535 if (uzbl.behave.download_handler) {
536 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
537 if (uzbl.state.verbose)
538 printf("Download -> %s\n",uri);
539 /* if urls not escaped, we may have to escape and quote uri before this call */
540 run_handler(uzbl.behave.download_handler, uri);
545 /* scroll a bar in a given direction */
547 scroll (GtkAdjustment* bar, GArray *argv) {
551 gdouble page_size = gtk_adjustment_get_page_size(bar);
552 gdouble value = gtk_adjustment_get_value(bar);
553 gdouble amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
556 value += page_size * amount * 0.01;
560 max_value = gtk_adjustment_get_upper(bar) - page_size;
562 if (value > max_value)
563 value = max_value; /* don't scroll past the end of the page */
565 gtk_adjustment_set_value (bar, value);
569 scroll_begin(WebKitWebView* page, GArray *argv, GString *result) {
570 (void) page; (void) argv; (void) result;
571 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
575 scroll_end(WebKitWebView* page, GArray *argv, GString *result) {
576 (void) page; (void) argv; (void) result;
577 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
578 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
582 scroll_vert(WebKitWebView* page, GArray *argv, GString *result) {
583 (void) page; (void) result;
584 scroll(uzbl.gui.bar_v, argv);
588 scroll_horz(WebKitWebView* page, GArray *argv, GString *result) {
589 (void) page; (void) result;
590 scroll(uzbl.gui.bar_h, argv);
595 if(!gtk_window_parse_geometry(GTK_WINDOW(uzbl.gui.main_window), uzbl.gui.geometry)) {
596 if(uzbl.state.verbose)
597 printf("Error in geometry string: %s\n", uzbl.gui.geometry);
599 /* update geometry var with the actual geometry
600 this is necessary as some WMs don't seem to honour
601 the above setting and we don't want to end up with
602 wrong geometry information
609 if (!uzbl.behave.show_status) {
610 gtk_widget_hide(uzbl.gui.mainbar);
612 gtk_widget_show(uzbl.gui.mainbar);
618 toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) {
623 webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page));
627 toggle_status_cb (WebKitWebView* page, GArray *argv, GString *result) {
632 if (uzbl.behave.show_status) {
633 gtk_widget_hide(uzbl.gui.mainbar);
635 gtk_widget_show(uzbl.gui.mainbar);
637 uzbl.behave.show_status = !uzbl.behave.show_status;
642 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
646 //Set selected_url state variable
647 g_free(uzbl.state.selected_url);
648 uzbl.state.selected_url = NULL;
650 uzbl.state.selected_url = g_strdup(link);
656 title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
659 const gchar *title = webkit_web_view_get_title(web_view);
660 if (uzbl.gui.main_title)
661 g_free (uzbl.gui.main_title);
662 uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
667 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
670 uzbl.gui.sbar.load_progress = progress;
675 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
679 if (uzbl.behave.load_finish_handler)
680 run_handler(uzbl.behave.load_finish_handler, "");
684 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
688 uzbl.gui.sbar.load_progress = 0;
689 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
690 if (uzbl.behave.load_start_handler)
691 run_handler(uzbl.behave.load_start_handler, "");
695 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
698 g_free (uzbl.state.uri);
699 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
700 uzbl.state.uri = g_string_free (newuri, FALSE);
701 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
702 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
705 if (uzbl.behave.load_commit_handler)
706 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
710 destroy_cb (GtkWidget* widget, gpointer data) {
718 if (uzbl.behave.history_handler) {
720 struct tm * timeinfo;
723 timeinfo = localtime ( &rawtime );
724 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
725 run_handler(uzbl.behave.history_handler, date);
730 /* VIEW funcs (little webkit wrappers) */
731 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv, GString *result){(void)argv; (void)result; webkit_web_view_##name(page);}
733 VIEWFUNC(reload_bypass_cache)
734 VIEWFUNC(stop_loading)
741 /* -- command to callback/function map for things we cannot attach to any signals */
742 static struct {char *key; CommandInfo value;} cmdlist[] =
743 { /* key function no_split */
744 { "back", {view_go_back, 0} },
745 { "forward", {view_go_forward, 0} },
746 { "scroll_vert", {scroll_vert, 0} },
747 { "scroll_horz", {scroll_horz, 0} },
748 { "scroll_begin", {scroll_begin, 0} },
749 { "scroll_end", {scroll_end, 0} },
750 { "reload", {view_reload, 0}, },
751 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
752 { "stop", {view_stop_loading, 0}, },
753 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
754 { "zoom_out", {view_zoom_out, 0}, },
755 { "toggle_zoom_type", {toggle_zoom_type, 0}, },
756 { "uri", {load_uri, TRUE} },
757 { "js", {run_js, TRUE} },
758 { "script", {run_external_js, 0} },
759 { "toggle_status", {toggle_status_cb, 0} },
760 { "spawn", {spawn, 0} },
761 { "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
762 { "sh", {spawn_sh, 0} },
763 { "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
764 { "exit", {close_uzbl, 0} },
765 { "search", {search_forward_text, TRUE} },
766 { "search_reverse", {search_reverse_text, TRUE} },
767 { "dehilight", {dehilight, 0} },
768 { "toggle_insert_mode", {toggle_insert_mode, 0} },
769 { "set", {set_var, TRUE} },
770 //{ "get", {get_var, TRUE} },
771 { "bind", {act_bind, TRUE} },
772 { "dump_config", {act_dump_config, 0} },
773 { "keycmd", {keycmd, TRUE} },
774 { "keycmd_nl", {keycmd_nl, TRUE} },
775 { "keycmd_bs", {keycmd_bs, 0} },
776 { "chain", {chain, 0} },
777 { "print", {print, TRUE} }
784 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
786 for (i = 0; i < LENGTH(cmdlist); i++)
787 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].key, &cmdlist[i].value);
790 /* -- CORE FUNCTIONS -- */
793 free_action(gpointer act) {
794 Action *action = (Action*)act;
795 g_free(action->name);
797 g_free(action->param);
802 new_action(const gchar *name, const gchar *param) {
803 Action *action = g_new(Action, 1);
805 action->name = g_strdup(name);
807 action->param = g_strdup(param);
809 action->param = NULL;
815 file_exists (const char * filename) {
816 return (access(filename, F_OK) == 0);
820 set_var(WebKitWebView *page, GArray *argv, GString *result) {
821 (void) page; (void) result;
822 gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
823 if (split[0] != NULL) {
824 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
825 set_var_value(g_strstrip(split[0]), value);
832 print(WebKitWebView *page, GArray *argv, GString *result) {
833 (void) page; (void) result;
836 buf = expand(argv_idx(argv, 0), 0);
837 g_string_assign(result, buf);
842 act_bind(WebKitWebView *page, GArray *argv, GString *result) {
843 (void) page; (void) result;
844 gchar **split = g_strsplit(argv_idx(argv, 0), " = ", 2);
845 gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
846 add_binding(g_strstrip(split[0]), value);
858 toggle_insert_mode(WebKitWebView *page, GArray *argv, GString *result) {
859 (void) page; (void) result;
861 if (argv_idx(argv, 0)) {
862 if (strcmp (argv_idx(argv, 0), "0") == 0) {
863 uzbl.behave.insert_mode = FALSE;
865 uzbl.behave.insert_mode = TRUE;
868 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
875 load_uri (WebKitWebView *web_view, GArray *argv, GString *result) {
878 if (argv_idx(argv, 0)) {
879 GString* newuri = g_string_new (argv_idx(argv, 0));
880 if (g_strstr_len (argv_idx(argv, 0), 11, "javascript:") != NULL) {
881 run_js(web_view, argv, NULL);
884 if (g_strrstr (argv_idx(argv, 0), "://") == NULL && g_strstr_len (argv_idx(argv, 0), 5, "data:") == NULL)
885 g_string_prepend (newuri, "http://");
886 /* if we do handle cookies, ask our handler for them */
887 webkit_web_view_load_uri (web_view, newuri->str);
888 g_string_free (newuri, TRUE);
896 js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
897 size_t argumentCount, const JSValueRef arguments[],
898 JSValueRef* exception) {
903 JSStringRef js_result_string;
904 GString *result = g_string_new("");
906 if (argumentCount >= 1) {
907 JSStringRef arg = JSValueToStringCopy(ctx, arguments[0], NULL);
908 size_t arg_size = JSStringGetMaximumUTF8CStringSize(arg);
909 char ctl_line[arg_size];
910 JSStringGetUTF8CString(arg, ctl_line, arg_size);
912 parse_cmd_line(ctl_line, result);
914 JSStringRelease(arg);
916 js_result_string = JSStringCreateWithUTF8CString(result->str);
918 g_string_free(result, TRUE);
920 return JSValueMakeString(ctx, js_result_string);
923 static JSStaticFunction js_static_functions[] = {
924 {"run", js_run_command, kJSPropertyAttributeNone},
929 /* This function creates the class and its definition, only once */
930 if (!uzbl.js.initialized) {
931 /* it would be pretty cool to make this dynamic */
932 uzbl.js.classdef = kJSClassDefinitionEmpty;
933 uzbl.js.classdef.staticFunctions = js_static_functions;
935 uzbl.js.classref = JSClassCreate(&uzbl.js.classdef);
941 eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
942 WebKitWebFrame *frame;
943 JSGlobalContextRef context;
944 JSObjectRef globalobject;
945 JSStringRef var_name;
947 JSStringRef js_script;
948 JSValueRef js_result;
949 JSStringRef js_result_string;
950 size_t js_result_size;
954 frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
955 context = webkit_web_frame_get_global_context(frame);
956 globalobject = JSContextGetGlobalObject(context);
958 /* uzbl javascript namespace */
959 var_name = JSStringCreateWithUTF8CString("Uzbl");
960 JSObjectSetProperty(context, globalobject, var_name,
961 JSObjectMake(context, uzbl.js.classref, NULL),
962 kJSClassAttributeNone, NULL);
964 /* evaluate the script and get return value*/
965 js_script = JSStringCreateWithUTF8CString(script);
966 js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
967 if (js_result && !JSValueIsUndefined(context, js_result)) {
968 js_result_string = JSValueToStringCopy(context, js_result, NULL);
969 js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
971 if (js_result_size) {
972 char js_result_utf8[js_result_size];
973 JSStringGetUTF8CString(js_result_string, js_result_utf8, js_result_size);
974 g_string_assign(result, js_result_utf8);
977 JSStringRelease(js_result_string);
981 JSObjectDeleteProperty(context, globalobject, var_name, NULL);
983 JSStringRelease(var_name);
984 JSStringRelease(js_script);
988 run_js (WebKitWebView * web_view, GArray *argv, GString *result) {
990 if (argv_idx(argv, 0))
991 eval_js(web_view, argv_idx(argv, 0), result);
995 run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
997 if (argv_idx(argv, 0)) {
998 GArray* lines = read_file_by_line (argv_idx (argv, 0));
1003 while ((line = g_array_index(lines, gchar*, i))) {
1005 js = g_strdup (line);
1007 gchar* newjs = g_strconcat (js, line, NULL);
1014 if (uzbl.state.verbose)
1015 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
1017 if (argv_idx (argv, 1)) {
1018 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
1022 eval_js (web_view, js, result);
1024 g_array_free (lines, TRUE);
1029 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
1030 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0')) {
1031 if (g_strcmp0 (uzbl.state.searchtx, argv_idx(argv, 0)) != 0) {
1032 webkit_web_view_unmark_text_matches (page);
1033 webkit_web_view_mark_text_matches (page, argv_idx(argv, 0), FALSE, 0);
1034 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
1038 if (uzbl.state.searchtx) {
1039 if (uzbl.state.verbose)
1040 printf ("Searching: %s\n", uzbl.state.searchtx);
1041 webkit_web_view_set_highlight_text_matches (page, TRUE);
1042 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
1047 search_forward_text (WebKitWebView *page, GArray *argv, GString *result) {
1049 search_text(page, argv, TRUE);
1053 search_reverse_text (WebKitWebView *page, GArray *argv, GString *result) {
1055 search_text(page, argv, FALSE);
1059 dehilight (WebKitWebView *page, GArray *argv, GString *result) {
1060 (void) argv; (void) result;
1061 webkit_web_view_set_highlight_text_matches (page, FALSE);
1066 new_window_load_uri (const gchar * uri) {
1067 GString* to_execute = g_string_new ("");
1068 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
1070 for (i = 0; entries[i].long_name != NULL; i++) {
1071 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
1072 gchar** str = (gchar**)entries[i].arg_data;
1074 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
1078 if (uzbl.state.verbose)
1079 printf("\n%s\n", to_execute->str);
1080 g_spawn_command_line_async (to_execute->str, NULL);
1081 g_string_free (to_execute, TRUE);
1085 chain (WebKitWebView *page, GArray *argv, GString *result) {
1086 (void) page; (void) result;
1088 gchar **parts = NULL;
1090 while ((a = argv_idx(argv, i++))) {
1091 parts = g_strsplit (a, " ", 2);
1092 parse_command(parts[0], parts[1], result);
1098 keycmd (WebKitWebView *page, GArray *argv, GString *result) {
1102 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1108 keycmd_nl (WebKitWebView *page, GArray *argv, GString *result) {
1112 g_string_assign(uzbl.state.keycmd, argv_idx(argv, 0));
1118 keycmd_bs (WebKitWebView *page, GArray *argv, GString *result) {
1123 prev = g_utf8_find_prev_char(uzbl.state.keycmd->str, uzbl.state.keycmd->str + uzbl.state.keycmd->len);
1125 g_string_truncate(uzbl.state.keycmd, prev - uzbl.state.keycmd->str);
1130 close_uzbl (WebKitWebView *page, GArray *argv, GString *result) {
1137 /* --Statusbar functions-- */
1139 build_progressbar_ascii(int percent) {
1140 int width=uzbl.gui.sbar.progress_w;
1143 GString *bar = g_string_new("");
1145 l = (double)percent*((double)width/100.);
1146 l = (int)(l+.5)>=(int)l ? l+.5 : l;
1148 for(i=0; i<(int)l; i++)
1149 g_string_append(bar, uzbl.gui.sbar.progress_s);
1152 g_string_append(bar, uzbl.gui.sbar.progress_u);
1154 return g_string_free(bar, FALSE);
1159 const GScannerConfig scan_config = {
1162 ) /* cset_skip_characters */,
1167 ) /* cset_identifier_first */,
1174 ) /* cset_identifier_nth */,
1175 ( "" ) /* cpair_comment_single */,
1177 TRUE /* case_sensitive */,
1179 FALSE /* skip_comment_multi */,
1180 FALSE /* skip_comment_single */,
1181 FALSE /* scan_comment_multi */,
1182 TRUE /* scan_identifier */,
1183 TRUE /* scan_identifier_1char */,
1184 FALSE /* scan_identifier_NULL */,
1185 TRUE /* scan_symbols */,
1186 FALSE /* scan_binary */,
1187 FALSE /* scan_octal */,
1188 FALSE /* scan_float */,
1189 FALSE /* scan_hex */,
1190 FALSE /* scan_hex_dollar */,
1191 FALSE /* scan_string_sq */,
1192 FALSE /* scan_string_dq */,
1193 TRUE /* numbers_2_int */,
1194 FALSE /* int_2_float */,
1195 FALSE /* identifier_2_string */,
1196 FALSE /* char_2_token */,
1197 FALSE /* symbol_2_token */,
1198 TRUE /* scope_0_fallback */,
1203 uzbl.scan = g_scanner_new(&scan_config);
1204 while(symp->symbol_name) {
1205 g_scanner_scope_add_symbol(uzbl.scan, 0,
1207 GINT_TO_POINTER(symp->symbol_token));
1213 expand_template(const char *template, gboolean escape_markup) {
1214 if(!template) return NULL;
1216 GTokenType token = G_TOKEN_NONE;
1217 GString *ret = g_string_new("");
1221 g_scanner_input_text(uzbl.scan, template, strlen(template));
1222 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
1223 token = g_scanner_get_next_token(uzbl.scan);
1225 if(token == G_TOKEN_SYMBOL) {
1226 sym = GPOINTER_TO_INT(g_scanner_cur_value(uzbl.scan).v_symbol);
1230 buf = uzbl.state.uri?
1231 g_markup_printf_escaped("%s", uzbl.state.uri):g_strdup("");
1232 g_string_append(ret, buf);
1236 g_string_append(ret, uzbl.state.uri?
1237 uzbl.state.uri:g_strdup(""));
1240 buf = itos(uzbl.gui.sbar.load_progress);
1241 g_string_append(ret, buf);
1244 case SYM_LOADPRGSBAR:
1245 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
1246 g_string_append(ret, buf);
1251 buf = uzbl.gui.main_title?
1252 g_markup_printf_escaped("%s", uzbl.gui.main_title):g_strdup("");
1253 g_string_append(ret, buf);
1257 g_string_append(ret, uzbl.gui.main_title?
1258 uzbl.gui.main_title:g_strdup(""));
1260 case SYM_SELECTED_URI:
1262 buf = uzbl.state.selected_url?
1263 g_markup_printf_escaped("%s", uzbl.state.selected_url):g_strdup("");
1264 g_string_append(ret, buf);
1268 g_string_append(ret, uzbl.state.selected_url?
1269 uzbl.state.selected_url:g_strdup(""));
1272 buf = itos(uzbl.xwin);
1273 g_string_append(ret,
1274 uzbl.state.instance_name?uzbl.state.instance_name:buf);
1279 buf = uzbl.state.keycmd->str?
1280 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):g_strdup("");
1281 g_string_append(ret, buf);
1285 g_string_append(ret, uzbl.state.keycmd->str?
1286 uzbl.state.keycmd->str:g_strdup(""));
1289 g_string_append(ret,
1290 uzbl.behave.insert_mode?
1291 uzbl.behave.insert_indicator:uzbl.behave.cmd_indicator);
1294 g_string_append(ret,
1295 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
1297 /* useragent syms */
1299 buf = itos(WEBKIT_MAJOR_VERSION);
1300 g_string_append(ret, buf);
1304 buf = itos(WEBKIT_MINOR_VERSION);
1305 g_string_append(ret, buf);
1309 buf = itos(WEBKIT_MICRO_VERSION);
1310 g_string_append(ret, buf);
1314 g_string_append(ret, uzbl.state.unameinfo.sysname);
1317 g_string_append(ret, uzbl.state.unameinfo.nodename);
1320 g_string_append(ret, uzbl.state.unameinfo.release);
1323 g_string_append(ret, uzbl.state.unameinfo.version);
1326 g_string_append(ret, uzbl.state.unameinfo.machine);
1329 g_string_append(ret, ARCH);
1332 case SYM_DOMAINNAME:
1333 g_string_append(ret, uzbl.state.unameinfo.domainname);
1337 g_string_append(ret, COMMIT);
1343 else if(token == G_TOKEN_INT) {
1344 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
1345 g_string_append(ret, buf);
1348 else if(token == G_TOKEN_IDENTIFIER) {
1349 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
1351 else if(token == G_TOKEN_CHAR) {
1352 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
1356 return g_string_free(ret, FALSE);
1358 /* --End Statusbar functions-- */
1361 sharg_append(GArray *a, const gchar *str) {
1362 const gchar *s = (str ? str : "");
1363 g_array_append_val(a, s);
1366 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
1368 run_command (const gchar *command, const guint npre, const gchar **args,
1369 const gboolean sync, char **output_stdout) {
1370 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
1373 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1374 gchar *pid = itos(getpid());
1375 gchar *xwin = itos(uzbl.xwin);
1377 sharg_append(a, command);
1378 for (i = 0; i < npre; i++) /* add n args before the default vars */
1379 sharg_append(a, args[i]);
1380 sharg_append(a, uzbl.state.config_file);
1381 sharg_append(a, pid);
1382 sharg_append(a, xwin);
1383 sharg_append(a, uzbl.comm.fifo_path);
1384 sharg_append(a, uzbl.comm.socket_path);
1385 sharg_append(a, uzbl.state.uri);
1386 sharg_append(a, uzbl.gui.main_title);
1388 for (i = npre; i < g_strv_length((gchar**)args); i++)
1389 sharg_append(a, args[i]);
1393 if (*output_stdout) *output_stdout = strfree(*output_stdout);
1395 result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1396 NULL, NULL, output_stdout, NULL, NULL, &err);
1397 } else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
1398 NULL, NULL, NULL, &err);
1400 if (uzbl.state.verbose) {
1401 GString *s = g_string_new("spawned:");
1402 for (i = 0; i < (a->len); i++) {
1403 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
1404 g_string_append_printf(s, " %s", qarg);
1407 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
1408 printf("%s\n", s->str);
1409 g_string_free(s, TRUE);
1411 printf("Stdout: %s\n", *output_stdout);
1415 g_printerr("error on run_command: %s\n", err->message);
1420 g_array_free (a, TRUE);
1425 split_quoted(const gchar* src, const gboolean unquote) {
1426 /* split on unquoted space, return array of strings;
1427 remove a layer of quotes and backslashes if unquote */
1428 if (!src) return NULL;
1430 gboolean dq = FALSE;
1431 gboolean sq = FALSE;
1432 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1433 GString *s = g_string_new ("");
1437 for (p = src; *p != '\0'; p++) {
1438 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
1439 else if (*p == '\\') { g_string_append_c(s, *p++);
1440 g_string_append_c(s, *p); }
1441 else if ((*p == '"') && unquote && !sq) dq = !dq;
1442 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
1444 else if ((*p == '\'') && unquote && !dq) sq = !sq;
1445 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
1447 else if ((*p == ' ') && !dq && !sq) {
1448 dup = g_strdup(s->str);
1449 g_array_append_val(a, dup);
1450 g_string_truncate(s, 0);
1451 } else g_string_append_c(s, *p);
1453 dup = g_strdup(s->str);
1454 g_array_append_val(a, dup);
1455 ret = (gchar**)a->data;
1456 g_array_free (a, FALSE);
1457 g_string_free (s, TRUE);
1462 spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
1463 (void)web_view; (void)result;
1464 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
1465 if (argv_idx(argv, 0))
1466 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
1470 spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1471 (void)web_view; (void)result;
1473 if (argv_idx(argv, 0))
1474 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
1475 TRUE, &uzbl.comm.sync_stdout);
1479 spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
1480 (void)web_view; (void)result;
1481 if (!uzbl.behave.shell_cmd) {
1482 g_printerr ("spawn_sh: shell_cmd is not set!\n");
1487 gchar *spacer = g_strdup("");
1488 g_array_insert_val(argv, 1, spacer);
1489 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1491 for (i = 1; i < g_strv_length(cmd); i++)
1492 g_array_prepend_val(argv, cmd[i]);
1494 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1500 spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
1501 (void)web_view; (void)result;
1502 if (!uzbl.behave.shell_cmd) {
1503 g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
1508 gchar *spacer = g_strdup("");
1509 g_array_insert_val(argv, 1, spacer);
1510 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
1512 for (i = 1; i < g_strv_length(cmd); i++)
1513 g_array_prepend_val(argv, cmd[i]);
1515 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
1516 TRUE, &uzbl.comm.sync_stdout);
1522 parse_command(const char *cmd, const char *param, GString *result) {
1525 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1527 gchar **par = split_quoted(param, TRUE);
1528 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1530 if (c->no_split) { /* don't split */
1531 sharg_append(a, param);
1533 for (i = 0; i < g_strv_length(par); i++)
1534 sharg_append(a, par[i]);
1537 if (result == NULL) {
1538 GString *result_print = g_string_new("");
1540 c->function(uzbl.gui.web_view, a, result_print);
1541 if (result_print->len)
1542 printf("%*s\n", result_print->len, result_print->str);
1544 g_string_free(result_print, TRUE);
1546 c->function(uzbl.gui.web_view, a, result);
1549 g_array_free (a, TRUE);
1552 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1559 if(*uzbl.net.proxy_url == ' '
1560 || uzbl.net.proxy_url == NULL) {
1561 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1562 (GType) SOUP_SESSION_PROXY_URI);
1565 suri = soup_uri_new(uzbl.net.proxy_url);
1566 g_object_set(G_OBJECT(uzbl.net.soup_session),
1567 SOUP_SESSION_PROXY_URI,
1569 soup_uri_free(suri);
1576 if(file_exists(uzbl.gui.icon)) {
1577 if (uzbl.gui.main_window)
1578 gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL);
1580 g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
1582 g_free (uzbl.gui.icon);
1587 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1588 g_array_append_val (a, uzbl.state.uri);
1589 load_uri(uzbl.gui.web_view, a, NULL);
1590 g_array_free (a, TRUE);
1594 cmd_always_insert_mode() {
1595 uzbl.behave.insert_mode =
1596 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1602 g_object_set(G_OBJECT(uzbl.net.soup_session),
1603 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1607 cmd_max_conns_host() {
1608 g_object_set(G_OBJECT(uzbl.net.soup_session),
1609 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1614 soup_session_remove_feature
1615 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1616 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1617 /*g_free(uzbl.net.soup_logger);*/
1619 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1620 soup_session_add_feature(uzbl.net.soup_session,
1621 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1624 static WebKitWebSettings*
1626 return webkit_web_view_get_settings(uzbl.gui.web_view);
1631 WebKitWebSettings *ws = view_settings();
1632 if (uzbl.behave.font_size > 0) {
1633 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
1636 if (uzbl.behave.monospace_size > 0) {
1637 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1638 uzbl.behave.monospace_size, NULL);
1640 g_object_set (G_OBJECT(ws), "default-monospace-font-size",
1641 uzbl.behave.font_size, NULL);
1647 webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level);
1651 cmd_disable_plugins() {
1652 g_object_set (G_OBJECT(view_settings()), "enable-plugins",
1653 !uzbl.behave.disable_plugins, NULL);
1657 cmd_disable_scripts() {
1658 g_object_set (G_OBJECT(view_settings()), "enable-scripts",
1659 !uzbl.behave.disable_scripts, NULL);
1663 cmd_minimum_font_size() {
1664 g_object_set (G_OBJECT(view_settings()), "minimum-font-size",
1665 uzbl.behave.minimum_font_size, NULL);
1668 cmd_autoload_img() {
1669 g_object_set (G_OBJECT(view_settings()), "auto-load-images",
1670 uzbl.behave.autoload_img, NULL);
1675 cmd_autoshrink_img() {
1676 g_object_set (G_OBJECT(view_settings()), "auto-shrink-images",
1677 uzbl.behave.autoshrink_img, NULL);
1682 cmd_enable_spellcheck() {
1683 g_object_set (G_OBJECT(view_settings()), "enable-spell-checking",
1684 uzbl.behave.enable_spellcheck, NULL);
1688 cmd_enable_private() {
1689 g_object_set (G_OBJECT(view_settings()), "enable-private-browsing",
1690 uzbl.behave.enable_private, NULL);
1695 g_object_set (G_OBJECT(view_settings()), "print-backgrounds",
1696 uzbl.behave.print_bg, NULL);
1701 g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
1702 uzbl.behave.style_uri, NULL);
1706 cmd_resizable_txt() {
1707 g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
1708 uzbl.behave.resizable_txt, NULL);
1712 cmd_default_encoding() {
1713 g_object_set (G_OBJECT(view_settings()), "default-encoding",
1714 uzbl.behave.default_encoding, NULL);
1718 cmd_enforce_96dpi() {
1719 g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
1720 uzbl.behave.enforce_96dpi, NULL);
1724 cmd_caret_browsing() {
1725 g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
1726 uzbl.behave.caret_browsing, NULL);
1730 cmd_cookie_handler() {
1731 gchar **split = g_strsplit(uzbl.behave.cookie_handler, " ", 2);
1732 /* pitfall: doesn't handle chain actions; must the sync_ action manually */
1733 if ((g_strcmp0(split[0], "sh") == 0) ||
1734 (g_strcmp0(split[0], "spawn") == 0)) {
1735 g_free (uzbl.behave.cookie_handler);
1736 uzbl.behave.cookie_handler =
1737 g_strdup_printf("sync_%s %s", split[0], split[1]);
1744 uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
1749 uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir);
1754 if(uzbl.behave.inject_html) {
1755 webkit_web_view_load_html_string (uzbl.gui.web_view,
1756 uzbl.behave.inject_html, NULL);
1765 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1766 uzbl.behave.modmask = 0;
1768 if(uzbl.behave.modkey)
1769 g_free(uzbl.behave.modkey);
1770 uzbl.behave.modkey = buf;
1772 for (i = 0; modkeys[i].key != NULL; i++) {
1773 if (g_strrstr(buf, modkeys[i].key))
1774 uzbl.behave.modmask |= modkeys[i].mask;
1780 if (*uzbl.net.useragent == ' ') {
1781 g_free (uzbl.net.useragent);
1782 uzbl.net.useragent = NULL;
1784 gchar *ua = expand_template(uzbl.net.useragent, FALSE);
1786 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1787 g_free(uzbl.net.useragent);
1788 uzbl.net.useragent = ua;
1794 gtk_widget_ref(uzbl.gui.scrolled_win);
1795 gtk_widget_ref(uzbl.gui.mainbar);
1796 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1797 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1799 if(uzbl.behave.status_top) {
1800 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1801 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1804 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1805 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1807 gtk_widget_unref(uzbl.gui.scrolled_win);
1808 gtk_widget_unref(uzbl.gui.mainbar);
1809 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1814 set_var_value(gchar *name, gchar *val) {
1815 uzbl_cmdprop *c = NULL;
1819 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1820 /* check for the variable type */
1821 if (c->type == TYPE_STR) {
1822 buf = expand(val, 0);
1825 } else if(c->type == TYPE_INT) {
1826 int *ip = (int *)c->ptr;
1827 buf = expand(val, 0);
1828 *ip = (int)strtoul(buf, &endp, 10);
1830 } else if (c->type == TYPE_FLOAT) {
1831 float *fp = (float *)c->ptr;
1832 buf = expand(val, 0);
1833 *fp = strtod(buf, &endp);
1837 /* invoke a command specific function */
1838 if(c->func) c->func();
1845 Behaviour *b = &uzbl.behave;
1847 if(b->html_buffer->str) {
1848 webkit_web_view_load_html_string (uzbl.gui.web_view,
1849 b->html_buffer->str, b->base_url);
1850 g_string_free(b->html_buffer, TRUE);
1851 b->html_buffer = g_string_new("");
1855 enum {M_CMD, M_HTML};
1857 parse_cmd_line(const char *ctl_line, GString *result) {
1858 Behaviour *b = &uzbl.behave;
1861 if(b->mode == M_HTML) {
1862 len = strlen(b->html_endmarker);
1863 /* ctl_line has trailing '\n' so we check for strlen(ctl_line)-1 */
1864 if(len == strlen(ctl_line)-1 &&
1865 !strncmp(b->html_endmarker, ctl_line, len)) {
1867 set_var_value("mode", "0");
1872 set_timeout(b->html_timeout);
1873 g_string_append(b->html_buffer, ctl_line);
1876 else if((ctl_line[0] == '#') /* Comments */
1877 || (ctl_line[0] == ' ')
1878 || (ctl_line[0] == '\n'))
1879 ; /* ignore these lines */
1880 else { /* parse a command */
1882 gchar **tokens = NULL;
1883 len = strlen(ctl_line);
1885 if (ctl_line[len - 1] == '\n') /* strip trailing newline */
1886 ctlstrip = g_strndup(ctl_line, len - 1);
1887 else ctlstrip = g_strdup(ctl_line);
1889 tokens = g_strsplit(ctlstrip, " ", 2);
1890 parse_command(tokens[0], tokens[1], result);
1897 build_stream_name(int type, const gchar* dir) {
1898 char *xwin_str = NULL;
1899 State *s = &uzbl.state;
1902 xwin_str = itos((int)uzbl.xwin);
1904 str = g_strdup_printf
1905 ("%s/uzbl_fifo_%s", dir,
1906 s->instance_name ? s->instance_name : xwin_str);
1907 } else if (type == SOCKET) {
1908 str = g_strdup_printf
1909 ("%s/uzbl_socket_%s", dir,
1910 s->instance_name ? s->instance_name : xwin_str );
1917 control_fifo(GIOChannel *gio, GIOCondition condition) {
1918 if (uzbl.state.verbose)
1919 printf("triggered\n");
1924 if (condition & G_IO_HUP)
1925 g_error ("Fifo: Read end of pipe died!\n");
1928 g_error ("Fifo: GIOChannel broke\n");
1930 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1931 if (ret == G_IO_STATUS_ERROR) {
1932 g_error ("Fifo: Error reading: %s\n", err->message);
1936 parse_cmd_line(ctl_line, NULL);
1943 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1944 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1945 if (unlink(uzbl.comm.fifo_path) == -1)
1946 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1947 g_free(uzbl.comm.fifo_path);
1948 uzbl.comm.fifo_path = NULL;
1951 if (*dir == ' ') { /* space unsets the variable */
1956 GIOChannel *chan = NULL;
1957 GError *error = NULL;
1958 gchar *path = build_stream_name(FIFO, dir);
1960 if (!file_exists(path)) {
1961 if (mkfifo (path, 0666) == 0) {
1962 // 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.
1963 chan = g_io_channel_new_file(path, "r+", &error);
1965 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1966 if (uzbl.state.verbose)
1967 printf ("init_fifo: created successfully as %s\n", path);
1968 uzbl.comm.fifo_path = path;
1970 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1971 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1972 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1973 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1975 /* if we got this far, there was an error; cleanup */
1976 if (error) g_error_free (error);
1983 control_stdin(GIOChannel *gio, GIOCondition condition) {
1985 gchar *ctl_line = NULL;
1988 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1989 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1992 parse_cmd_line(ctl_line, NULL);
2000 GIOChannel *chan = NULL;
2001 GError *error = NULL;
2003 chan = g_io_channel_unix_new(fileno(stdin));
2005 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
2006 g_error ("Stdin: could not add watch\n");
2008 if (uzbl.state.verbose)
2009 printf ("Stdin: watch added successfully\n");
2012 g_error ("Stdin: Error while opening: %s\n", error->message);
2014 if (error) g_error_free (error);
2018 control_socket(GIOChannel *chan) {
2019 struct sockaddr_un remote;
2020 unsigned int t = sizeof(remote);
2022 GIOChannel *clientchan;
2024 clientsock = accept (g_io_channel_unix_get_fd(chan),
2025 (struct sockaddr *) &remote, &t);
2027 if ((clientchan = g_io_channel_unix_new(clientsock))) {
2028 g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
2029 (GIOFunc) control_client_socket, clientchan);
2036 control_client_socket(GIOChannel *clientchan) {
2038 GString *result = g_string_new("");
2039 GError *error = NULL;
2043 ret = g_io_channel_read_line(clientchan, &ctl_line, &len, NULL, &error);
2044 if (ret == G_IO_STATUS_ERROR) {
2045 g_warning ("Error reading: %s\n", error->message);
2046 g_io_channel_shutdown(clientchan, TRUE, &error);
2048 } else if (ret == G_IO_STATUS_EOF) {
2049 /* shutdown and remove channel watch from main loop */
2050 g_io_channel_shutdown(clientchan, TRUE, &error);
2055 parse_cmd_line (ctl_line, result);
2056 g_string_append_c(result, '\n');
2057 ret = g_io_channel_write_chars (clientchan, result->str, result->len,
2059 if (ret == G_IO_STATUS_ERROR) {
2060 g_warning ("Error writing: %s", error->message);
2062 g_io_channel_flush(clientchan, &error);
2065 if (error) g_error_free (error);
2066 g_string_free(result, TRUE);
2072 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
2073 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
2074 if (unlink(uzbl.comm.socket_path) == -1)
2075 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
2076 g_free(uzbl.comm.socket_path);
2077 uzbl.comm.socket_path = NULL;
2085 GIOChannel *chan = NULL;
2087 struct sockaddr_un local;
2088 gchar *path = build_stream_name(SOCKET, dir);
2090 sock = socket (AF_UNIX, SOCK_STREAM, 0);
2092 local.sun_family = AF_UNIX;
2093 strcpy (local.sun_path, path);
2094 unlink (local.sun_path);
2096 len = strlen (local.sun_path) + sizeof (local.sun_family);
2097 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
2098 if (uzbl.state.verbose)
2099 printf ("init_socket: opened in %s\n", path);
2102 if( (chan = g_io_channel_unix_new(sock)) ) {
2103 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
2104 uzbl.comm.socket_path = path;
2107 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
2109 /* if we got this far, there was an error; cleanup */
2116 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
2117 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
2119 // this function may be called very early when the templates are not set (yet), hence the checks
2121 update_title (void) {
2122 Behaviour *b = &uzbl.behave;
2125 if (b->show_status) {
2126 if (b->title_format_short) {
2127 parsed = expand_template(b->title_format_short, FALSE);
2128 if (uzbl.gui.main_window)
2129 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2132 if (b->status_format) {
2133 parsed = expand_template(b->status_format, TRUE);
2134 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
2137 if (b->status_background) {
2139 gdk_color_parse (b->status_background, &color);
2140 //labels and hboxes do not draw their own background. applying this on the window/vbox is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
2141 if (uzbl.gui.main_window)
2142 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
2143 else if (uzbl.gui.plug)
2144 gtk_widget_modify_bg ((GtkWidget * ) uzbl.gui.plug, GTK_STATE_NORMAL, &color);
2147 if (b->title_format_long) {
2148 parsed = expand_template(b->title_format_long, FALSE);
2149 if (uzbl.gui.main_window)
2150 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
2157 configure_event_cb(GtkWidget* window, GdkEventConfigure* event) {
2161 retreive_geometry();
2166 key_press_cb (GtkWidget* window, GdkEventKey* event)
2168 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
2172 if (event->type != GDK_KEY_PRESS ||
2173 event->keyval == GDK_Page_Up ||
2174 event->keyval == GDK_Page_Down ||
2175 event->keyval == GDK_Up ||
2176 event->keyval == GDK_Down ||
2177 event->keyval == GDK_Left ||
2178 event->keyval == GDK_Right ||
2179 event->keyval == GDK_Shift_L ||
2180 event->keyval == GDK_Shift_R)
2183 /* turn off insert mode (if always_insert_mode is not used) */
2184 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
2185 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
2190 if (uzbl.behave.insert_mode &&
2191 ( ((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) ||
2192 (!uzbl.behave.modmask)
2197 if (event->keyval == GDK_Escape) {
2198 g_string_truncate(uzbl.state.keycmd, 0);
2200 dehilight(uzbl.gui.web_view, NULL, NULL);
2204 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
2205 if (event->keyval == GDK_Insert) {
2207 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
2208 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
2210 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
2213 g_string_append (uzbl.state.keycmd, str);
2220 if (event->keyval == GDK_BackSpace)
2221 keycmd_bs(NULL, NULL, NULL);
2223 gboolean key_ret = FALSE;
2224 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
2226 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
2228 run_keycmd(key_ret);
2230 if (key_ret) return (!uzbl.behave.insert_mode);
2235 run_keycmd(const gboolean key_ret) {
2236 /* run the keycmd immediately if it isn't incremental and doesn't take args */
2238 if ((act = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
2239 g_string_truncate(uzbl.state.keycmd, 0);
2240 parse_command(act->name, act->param, NULL);
2244 /* try if it's an incremental keycmd or one that takes args, and run it */
2245 GString* short_keys = g_string_new ("");
2246 GString* short_keys_inc = g_string_new ("");
2248 for (i=0; i<(uzbl.state.keycmd->len); i++) {
2249 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
2250 g_string_assign(short_keys_inc, short_keys->str);
2251 g_string_append_c(short_keys, '_');
2252 g_string_append_c(short_keys_inc, '*');
2254 if (key_ret && (act = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
2255 /* run normal cmds only if return was pressed */
2256 exec_paramcmd(act, i);
2257 g_string_truncate(uzbl.state.keycmd, 0);
2259 } else if ((act = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
2260 if (key_ret) /* just quit the incremental command on return */
2261 g_string_truncate(uzbl.state.keycmd, 0);
2262 else exec_paramcmd(act, i); /* otherwise execute the incremental */
2266 g_string_truncate(short_keys, short_keys->len - 1);
2268 g_string_free (short_keys, TRUE);
2269 g_string_free (short_keys_inc, TRUE);
2273 exec_paramcmd(const Action *act, const guint i) {
2274 GString *parampart = g_string_new (uzbl.state.keycmd->str);
2275 GString *actionname = g_string_new ("");
2276 GString *actionparam = g_string_new ("");
2277 g_string_erase (parampart, 0, i+1);
2279 g_string_printf (actionname, act->name, parampart->str);
2281 g_string_printf (actionparam, act->param, parampart->str);
2282 parse_command(actionname->str, actionparam->str, NULL);
2283 g_string_free(actionname, TRUE);
2284 g_string_free(actionparam, TRUE);
2285 g_string_free(parampart, TRUE);
2293 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2294 //main_window_ref = g_object_ref(scrolled_window);
2295 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
2297 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
2298 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
2300 g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
2301 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
2302 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
2303 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
2304 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
2305 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
2306 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
2307 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
2308 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
2309 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
2310 g_signal_connect (G_OBJECT (g->web_view), "mime-type-policy-decision-requested", G_CALLBACK (mime_policy_cb), g->web_view);
2312 return scrolled_window;
2319 g->mainbar = gtk_hbox_new (FALSE, 0);
2321 /* keep a reference to the bar so we can re-pack it at runtime*/
2322 //sbar_ref = g_object_ref(g->mainbar);
2324 g->mainbar_label = gtk_label_new ("");
2325 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
2326 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
2327 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
2328 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
2329 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
2330 g_signal_connect (G_OBJECT (g->mainbar), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2335 GtkWidget* create_window () {
2336 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2337 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
2338 gtk_widget_set_name (window, "Uzbl browser");
2339 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
2340 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2341 g_signal_connect (G_OBJECT (window), "configure-event", G_CALLBACK (configure_event_cb), NULL);
2347 GtkPlug* create_plug () {
2348 GtkPlug* plug = GTK_PLUG (gtk_plug_new (uzbl.state.socket_id));
2349 g_signal_connect (G_OBJECT (plug), "destroy", G_CALLBACK (destroy_cb), NULL);
2350 g_signal_connect (G_OBJECT (plug), "key-press-event", G_CALLBACK (key_press_cb), NULL);
2357 inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *newargs) {
2359 If actname is one that calls an external command, this function will inject
2360 newargs in front of the user-provided args in that command line. They will
2361 come become after the body of the script (in sh) or after the name of
2362 the command to execute (in spawn).
2363 i.e. sh <body> <userargs> becomes sh <body> <ARGS> <userargs> and
2364 span <command> <userargs> becomes spawn <command> <ARGS> <userargs>.
2366 The return value consist of two strings: the action (sh, ...) and its args.
2368 If act is not one that calls an external command, then the given action merely
2371 GArray *rets = g_array_new(TRUE, FALSE, sizeof(gchar*));
2372 gchar *actdup = g_strdup(actname);
2373 g_array_append_val(rets, actdup);
2375 if ((g_strcmp0(actname, "spawn") == 0) ||
2376 (g_strcmp0(actname, "sh") == 0) ||
2377 (g_strcmp0(actname, "sync_spawn") == 0) ||
2378 (g_strcmp0(actname, "sync_sh") == 0)) {
2380 GString *a = g_string_new("");
2381 gchar **spawnparts = split_quoted(origargs, FALSE);
2382 g_string_append_printf(a, "%s", spawnparts[0]); /* sh body or script name */
2383 if (newargs) g_string_append_printf(a, " %s", newargs); /* handler args */
2385 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
2386 if (spawnparts[i]) g_string_append_printf(a, " %s", spawnparts[i]);
2388 g_array_append_val(rets, a->str);
2389 g_string_free(a, FALSE);
2390 g_strfreev(spawnparts);
2392 gchar *origdup = g_strdup(origargs);
2393 g_array_append_val(rets, origdup);
2395 return (gchar**)g_array_free(rets, FALSE);
2399 run_handler (const gchar *act, const gchar *args) {
2400 /* Consider this code a temporary hack to make the handlers usable.
2401 In practice, all this splicing, injection, and reconstruction is
2402 inefficient, annoying and hard to manage. Potential pitfalls arise
2403 when the handler specific args 1) are not quoted (the handler
2404 callbacks should take care of this) 2) are quoted but interfere
2405 with the users' own quotation. A more ideal solution is
2406 to refactor parse_command so that it doesn't just take a string
2407 and execute it; rather than that, we should have a function which
2408 returns the argument vector parsed from the string. This vector
2409 could be modified (e.g. insert additional args into it) before
2410 passing it to the next function that actually executes it. Though
2411 it still isn't perfect for chain actions.. will reconsider & re-
2412 factor when I have the time. -duc */
2414 char **parts = g_strsplit(act, " ", 2);
2416 if (g_strcmp0(parts[0], "chain") == 0) {
2417 GString *newargs = g_string_new("");
2418 gchar **chainparts = split_quoted(parts[1], FALSE);
2420 /* for every argument in the chain, inject the handler args
2421 and make sure the new parts are wrapped in quotes */
2422 gchar **cp = chainparts;
2424 gchar *quotless = NULL;
2425 gchar **spliced_quotless = NULL; // sigh -_-;
2426 gchar **inpart = NULL;
2429 if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
2431 quotless = g_strndup(&(*cp)[1], strlen(*cp) - 2);
2432 } else quotless = g_strdup(*cp);
2434 spliced_quotless = g_strsplit(quotless, " ", 2);
2435 inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
2436 g_strfreev(spliced_quotless);
2438 g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
2444 parse_command(parts[0], &(newargs->str[1]), NULL);
2445 g_string_free(newargs, TRUE);
2446 g_strfreev(chainparts);
2449 gchar **inparts = inject_handler_args(parts[0], parts[1], args);
2450 parse_command(inparts[0], inparts[1], NULL);
2458 add_binding (const gchar *key, const gchar *act) {
2459 char **parts = g_strsplit(act, " ", 2);
2466 if (uzbl.state.verbose)
2467 printf ("Binding %-10s : %s\n", key, act);
2468 action = new_action(parts[0], parts[1]);
2470 if (g_hash_table_remove (uzbl.bindings, key))
2471 g_warning ("Overwriting existing binding for \"%s\"", key);
2472 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
2477 get_xdg_var (XDG_Var xdg) {
2478 const gchar* actual_value = getenv (xdg.environmental);
2479 const gchar* home = getenv ("HOME");
2480 gchar* return_value;
2482 if (! actual_value || strcmp (actual_value, "") == 0) {
2483 if (xdg.default_value) {
2484 return_value = str_replace ("~", home, xdg.default_value);
2486 return_value = NULL;
2489 return_value = str_replace("~", home, actual_value);
2492 return return_value;
2496 find_xdg_file (int xdg_type, char* filename) {
2497 /* xdg_type = 0 => config
2498 xdg_type = 1 => data
2499 xdg_type = 2 => cache*/
2501 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
2502 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
2505 gchar* temporary_string;
2509 if (! file_exists (temporary_file) && xdg_type != 2) {
2510 buf = get_xdg_var (XDG[3 + xdg_type]);
2511 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
2514 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
2515 g_free (temporary_file);
2516 temporary_file = g_strconcat (temporary_string, filename, NULL);
2520 //g_free (temporary_string); - segfaults.
2522 if (file_exists (temporary_file)) {
2523 return temporary_file;
2530 State *s = &uzbl.state;
2531 Network *n = &uzbl.net;
2533 for (i = 0; default_config[i].command != NULL; i++) {
2534 parse_cmd_line(default_config[i].command, NULL);
2537 if (g_strcmp0(s->config_file, "-") == 0) {
2538 s->config_file = NULL;
2542 else if (!s->config_file) {
2543 s->config_file = find_xdg_file (0, "/uzbl/config");
2546 if (s->config_file) {
2547 GArray* lines = read_file_by_line (s->config_file);
2551 while ((line = g_array_index(lines, gchar*, i))) {
2552 parse_cmd_line (line, NULL);
2556 g_array_free (lines, TRUE);
2558 if (uzbl.state.verbose)
2559 printf ("No configuration file loaded.\n");
2562 g_signal_connect_after(n->soup_session, "request-started", G_CALLBACK(handle_cookies), NULL);
2565 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
2568 if (!uzbl.behave.cookie_handler)
2571 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
2572 GString *s = g_string_new ("");
2573 SoupURI * soup_uri = soup_message_get_uri(msg);
2574 g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
2575 run_handler(uzbl.behave.cookie_handler, s->str);
2577 if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
2578 char *p = strchr(uzbl.comm.sync_stdout, '\n' );
2579 if ( p != NULL ) *p = '\0';
2580 soup_message_headers_replace (msg->request_headers, "Cookie", (const char *) uzbl.comm.sync_stdout);
2582 if (uzbl.comm.sync_stdout)
2583 uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
2585 g_string_free(s, TRUE);
2589 save_cookies (SoupMessage *msg, gpointer user_data){
2593 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
2594 cookie = soup_cookie_to_set_cookie_header(ck->data);
2595 SoupURI * soup_uri = soup_message_get_uri(msg);
2596 GString *s = g_string_new ("");
2597 g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
2598 run_handler(uzbl.behave.cookie_handler, s->str);
2600 g_string_free(s, TRUE);
2605 /* --- WEBINSPECTOR --- */
2607 hide_window_cb(GtkWidget *widget, gpointer data) {
2610 gtk_widget_hide(widget);
2613 static WebKitWebView*
2614 create_inspector_cb (WebKitWebInspector* web_inspector, WebKitWebView* page, gpointer data){
2617 (void) web_inspector;
2618 GtkWidget* scrolled_window;
2619 GtkWidget* new_web_view;
2622 g->inspector_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2623 g_signal_connect(G_OBJECT(g->inspector_window), "delete-event",
2624 G_CALLBACK(hide_window_cb), NULL);
2626 gtk_window_set_title(GTK_WINDOW(g->inspector_window), "Uzbl WebInspector");
2627 gtk_window_set_default_size(GTK_WINDOW(g->inspector_window), 400, 300);
2628 gtk_widget_show(g->inspector_window);
2630 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2631 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2632 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2633 gtk_container_add(GTK_CONTAINER(g->inspector_window), scrolled_window);
2634 gtk_widget_show(scrolled_window);
2636 new_web_view = webkit_web_view_new();
2637 gtk_container_add(GTK_CONTAINER(scrolled_window), new_web_view);
2639 return WEBKIT_WEB_VIEW(new_web_view);
2643 inspector_show_window_cb (WebKitWebInspector* inspector){
2645 gtk_widget_show(uzbl.gui.inspector_window);
2649 /* TODO: Add variables and code to make use of these functions */
2651 inspector_close_window_cb (WebKitWebInspector* inspector){
2657 inspector_attach_window_cb (WebKitWebInspector* inspector){
2663 inspector_detach_window_cb (WebKitWebInspector* inspector){
2669 inspector_uri_changed_cb (WebKitWebInspector* inspector){
2675 inspector_inspector_destroyed_cb (WebKitWebInspector* inspector){
2681 set_up_inspector() {
2683 WebKitWebSettings *settings = view_settings();
2684 g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
2686 uzbl.gui.inspector = webkit_web_view_get_inspector(uzbl.gui.web_view);
2687 g_signal_connect (G_OBJECT (g->inspector), "inspect-web-view", G_CALLBACK (create_inspector_cb), NULL);
2688 g_signal_connect (G_OBJECT (g->inspector), "show-window", G_CALLBACK (inspector_show_window_cb), NULL);
2689 g_signal_connect (G_OBJECT (g->inspector), "close-window", G_CALLBACK (inspector_close_window_cb), NULL);
2690 g_signal_connect (G_OBJECT (g->inspector), "attach-window", G_CALLBACK (inspector_attach_window_cb), NULL);
2691 g_signal_connect (G_OBJECT (g->inspector), "detach-window", G_CALLBACK (inspector_detach_window_cb), NULL);
2692 g_signal_connect (G_OBJECT (g->inspector), "finished", G_CALLBACK (inspector_inspector_destroyed_cb), NULL);
2694 g_signal_connect (G_OBJECT (g->inspector), "notify::inspected-uri", G_CALLBACK (inspector_uri_changed_cb), NULL);
2698 dump_var_hash(gpointer k, gpointer v, gpointer ud) {
2700 uzbl_cmdprop *c = v;
2705 if(c->type == TYPE_STR)
2706 printf("set %s = %s\n", (char *)k, *c->ptr?(char *)*c->ptr:" ");
2707 else if(c->type == TYPE_INT)
2708 printf("set %s = %d\n", (char *)k, (int)*c->ptr);
2712 dump_key_hash(gpointer k, gpointer v, gpointer ud) {
2716 printf("bind %s = %s %s\n", (char *)k ,
2717 (char *)a->name, a->param?(char *)a->param:"");
2721 dump_config() { //ADD "result" var so we can use this with uzblctrl
2722 g_hash_table_foreach(uzbl.comm.proto_var, dump_var_hash, NULL);
2723 g_hash_table_foreach(uzbl.bindings, dump_key_hash, NULL);
2727 retreive_geometry() {
2729 GString *buf = g_string_new("");
2731 gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h);
2732 gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y);
2734 g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y);
2736 if(uzbl.gui.geometry)
2737 g_free(uzbl.gui.geometry);
2738 uzbl.gui.geometry = g_string_free(buf, FALSE);
2743 main (int argc, char* argv[]) {
2744 gtk_init (&argc, &argv);
2745 if (!g_thread_supported ())
2746 g_thread_init (NULL);
2747 uzbl.state.executable_path = g_strdup(argv[0]);
2748 uzbl.state.selected_url = NULL;
2749 uzbl.state.searchtx = NULL;
2751 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
2752 g_option_context_add_main_entries (context, entries, NULL);
2753 g_option_context_add_group (context, gtk_get_option_group (TRUE));
2754 g_option_context_parse (context, &argc, &argv, NULL);
2755 g_option_context_free(context);
2757 gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
2758 gboolean verbose_override = uzbl.state.verbose;
2760 /* initialize hash table */
2761 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
2763 uzbl.net.soup_session = webkit_get_default_session();
2764 uzbl.state.keycmd = g_string_new("");
2766 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
2767 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
2768 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
2769 fprintf(stderr, "uzbl: error hooking SIGINT\n");
2770 if(setup_signal(SIGALRM, catch_alrm) == SIG_ERR)
2771 fprintf(stderr, "uzbl: error hooking SIGALARM\n");
2774 if(uname(&uzbl.state.unameinfo) == -1)
2775 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
2777 uzbl.gui.sbar.progress_s = g_strdup("=");
2778 uzbl.gui.sbar.progress_u = g_strdup("·");
2779 uzbl.gui.sbar.progress_w = 10;
2781 /* HTML mode defaults*/
2782 uzbl.behave.html_buffer = g_string_new("");
2783 uzbl.behave.html_endmarker = g_strdup(".");
2784 uzbl.behave.html_timeout = 60;
2785 uzbl.behave.base_url = g_strdup("http://invalid");
2787 /* default mode indicators */
2788 uzbl.behave.insert_indicator = g_strdup("I");
2789 uzbl.behave.cmd_indicator = g_strdup("C");
2793 make_var_to_name_hash();
2795 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
2797 uzbl.gui.scrolled_win = create_browser();
2800 /* initial packing */
2801 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
2802 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
2804 if (uzbl.state.socket_id) {
2805 uzbl.gui.plug = create_plug ();
2806 gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox);
2807 gtk_widget_show_all (GTK_WIDGET (uzbl.gui.plug));
2809 uzbl.gui.main_window = create_window ();
2810 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
2811 gtk_widget_show_all (uzbl.gui.main_window);
2812 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
2815 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
2817 if (uzbl.state.verbose) {
2818 printf("Uzbl start location: %s\n", argv[0]);
2819 if (uzbl.state.socket_id)
2820 printf("plug_id %i\n", gtk_plug_get_id(uzbl.gui.plug));
2822 printf("window_id %i\n",(int) uzbl.xwin);
2823 printf("pid %i\n", getpid ());
2824 printf("name: %s\n", uzbl.state.instance_name);
2827 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
2828 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
2829 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
2830 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
2831 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
2833 if(uzbl.gui.geometry)
2836 retreive_geometry();
2840 if (!uzbl.behave.show_status)
2841 gtk_widget_hide(uzbl.gui.mainbar);
2848 if (verbose_override > uzbl.state.verbose)
2849 uzbl.state.verbose = verbose_override;
2852 set_var_value("uri", uri_override);
2853 g_free(uri_override);
2854 } else if (uzbl.state.uri)
2855 cmd_load_uri(uzbl.gui.web_view, NULL);
2860 return EXIT_SUCCESS;
2863 /* vi: set et ts=4: */