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
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
61 /* define names and pointers to all config specific variables */
65 } var_name_to_ptr[] = {
66 { "uri", (void *)&uzbl.state.uri },
67 { "status_message", (void *)&uzbl.gui.sbar.msg },
68 { "show_status", (void *)&uzbl.behave.show_status },
69 { "status_top", (void *)&uzbl.behave.status_top },
70 { "status_format", (void *)&uzbl.behave.status_format },
71 { "status_background", (void *)&uzbl.behave.status_background },
72 { "title_format_long", (void *)&uzbl.behave.title_format_long },
73 { "title_format_short", (void *)&uzbl.behave.title_format_short },
74 { "insert_mode", (void *)&uzbl.behave.insert_mode },
75 { "always_insert_mode", (void *)&uzbl.behave.always_insert_mode },
76 { "reset_command_mode", (void *)&uzbl.behave.reset_command_mode },
77 { "modkey" , (void *)&uzbl.behave.modkey },
78 { "load_finish_handler",(void *)&uzbl.behave.load_finish_handler},
79 { "load_start_handler", (void *)&uzbl.behave.load_start_handler },
80 { "load_commit_handler",(void *)&uzbl.behave.load_commit_handler},
81 { "history_handler", (void *)&uzbl.behave.history_handler },
82 { "download_handler", (void *)&uzbl.behave.download_handler },
83 { "cookie_handler", (void *)&uzbl.behave.cookie_handler },
84 { "fifo_dir", (void *)&uzbl.behave.fifo_dir },
85 { "socket_dir", (void *)&uzbl.behave.socket_dir },
86 { "http_debug", (void *)&uzbl.behave.http_debug },
87 { "default_font_size", (void *)&uzbl.behave.default_font_size },
88 { "minimum_font_size", (void *)&uzbl.behave.minimum_font_size },
89 { "shell_cmd", (void *)&uzbl.behave.shell_cmd },
90 { "proxy_url", (void *)&uzbl.net.proxy_url },
91 { "max_conns", (void *)&uzbl.net.max_conns },
92 { "max_conns_host", (void *)&uzbl.net.max_conns_host },
93 { "useragent", (void *)&uzbl.net.useragent },
95 }, *n2v_p = var_name_to_ptr;
101 { "SHIFT", GDK_SHIFT_MASK }, // shift
102 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
103 { "CONTROL", GDK_CONTROL_MASK }, // control
104 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
105 { "MOD2", GDK_MOD2_MASK }, // 5th mod
106 { "MOD3", GDK_MOD3_MASK }, // 6th mod
107 { "MOD4", GDK_MOD4_MASK }, // 7th mod
108 { "MOD5", GDK_MOD5_MASK }, // 8th mod
109 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
110 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
111 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
112 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
113 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
114 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
115 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
116 { "META", GDK_META_MASK }, // meta (since 2.10)
120 /* construct a hash from the var_name_to_ptr array for quick access */
122 make_var_to_name_hash() {
123 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
125 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
130 /* commandline arguments (set initial values for the state variables) */
131 static GOptionEntry entries[] =
133 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
134 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
135 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
136 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
137 { NULL, 0, 0, 0, NULL, NULL, NULL }
140 typedef void (*Command)(WebKitWebView*, const char *);
142 /* --- UTILITY FUNCTIONS --- */
148 snprintf(tmp, sizeof(tmp), "%i", val);
149 return g_strdup(tmp);
153 str_replace (const char* search, const char* replace, const char* string) {
157 buf = g_strsplit (string, search, -1);
158 ret = g_strjoinv (replace, buf);
165 setup_signal(int signr, sigfunc *shandler) {
166 struct sigaction nh, oh;
168 nh.sa_handler = shandler;
169 sigemptyset(&nh.sa_mask);
172 if(sigaction(signr, &nh, &oh) < 0)
180 if (uzbl.behave.fifo_dir)
181 unlink (uzbl.comm.fifo_path);
182 if (uzbl.behave.socket_dir)
183 unlink (uzbl.comm.socket_path);
185 g_free(uzbl.state.executable_path);
186 g_string_free(uzbl.state.keycmd, TRUE);
187 g_hash_table_destroy(uzbl.bindings);
188 g_hash_table_destroy(uzbl.behave.commands);
192 /* --- SIGNAL HANDLER --- */
195 catch_sigterm(int s) {
201 catch_sigint(int s) {
207 /* --- CALLBACKS --- */
210 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
213 (void) navigation_action;
214 (void) policy_decision;
216 const gchar* uri = webkit_network_request_get_uri (request);
217 if (uzbl.state.verbose)
218 printf("New window requested -> %s \n", uri);
219 new_window_load_uri(uri);
224 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
228 if (uzbl.state.selected_url != NULL) {
229 if (uzbl.state.verbose)
230 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
231 new_window_load_uri(uzbl.state.selected_url);
233 if (uzbl.state.verbose)
234 printf("New web view -> %s\n","Nothing to open, exiting");
240 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
243 if (uzbl.behave.download_handler) {
244 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
245 if (uzbl.state.verbose)
246 printf("Download -> %s\n",uri);
247 /* if urls not escaped, we may have to escape and quote uri before this call */
248 run_handler(uzbl.behave.download_handler, uri);
253 /* scroll a bar in a given direction */
255 scroll (GtkAdjustment* bar, const char *param) {
259 amount = g_ascii_strtod(param, &end);
262 fprintf(stderr, "found something after double: %s\n", end);
264 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
267 static void scroll_begin(WebKitWebView* page, const char *param) {
268 (void) page; (void) param;
269 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
272 static void scroll_end(WebKitWebView* page, const char *param) {
273 (void) page; (void) param;
274 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
275 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
278 static void scroll_vert(WebKitWebView* page, const char *param) {
280 scroll(uzbl.gui.bar_v, param);
283 static void scroll_horz(WebKitWebView* page, const char *param) {
285 scroll(uzbl.gui.bar_h, param);
290 if (!uzbl.behave.show_status) {
291 gtk_widget_hide(uzbl.gui.mainbar);
293 gtk_widget_show(uzbl.gui.mainbar);
299 toggle_status_cb (WebKitWebView* page, const char *param) {
303 if (uzbl.behave.show_status) {
304 gtk_widget_hide(uzbl.gui.mainbar);
306 gtk_widget_show(uzbl.gui.mainbar);
308 uzbl.behave.show_status = !uzbl.behave.show_status;
313 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
317 //Set selected_url state variable
318 g_free(uzbl.state.selected_url);
319 uzbl.state.selected_url = NULL;
321 uzbl.state.selected_url = g_strdup(link);
327 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
331 if (uzbl.gui.main_title)
332 g_free (uzbl.gui.main_title);
333 uzbl.gui.main_title = g_strdup (title);
338 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
341 uzbl.gui.sbar.load_progress = progress;
346 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
350 if (uzbl.behave.load_finish_handler)
351 run_handler(uzbl.behave.load_finish_handler, "");
355 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
359 if (uzbl.behave.load_start_handler)
360 run_handler(uzbl.behave.load_start_handler, "");
364 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
367 free (uzbl.state.uri);
368 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
369 uzbl.state.uri = g_string_free (newuri, FALSE);
370 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
371 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
374 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
375 if (uzbl.behave.load_commit_handler)
376 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
380 destroy_cb (GtkWidget* widget, gpointer data) {
388 if (uzbl.behave.history_handler) {
390 struct tm * timeinfo;
393 timeinfo = localtime ( &rawtime );
394 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
395 run_handler(uzbl.behave.history_handler, date);
400 /* VIEW funcs (little webkit wrappers) */
401 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
403 VIEWFUNC(reload_bypass_cache)
404 VIEWFUNC(stop_loading)
411 /* -- command to callback/function map for things we cannot attach to any signals */
414 static struct {char *name; Command command;} cmdlist[] =
416 { "back", view_go_back },
417 { "forward", view_go_forward },
418 { "scroll_vert", scroll_vert },
419 { "scroll_horz", scroll_horz },
420 { "scroll_begin", scroll_begin },
421 { "scroll_end", scroll_end },
422 { "reload", view_reload, },
423 { "reload_ign_cache", view_reload_bypass_cache},
424 { "stop", view_stop_loading, },
425 { "zoom_in", view_zoom_in, }, //Can crash (when max zoom reached?).
426 { "zoom_out", view_zoom_out, },
428 { "script", run_js },
429 { "toggle_status", toggle_status_cb },
432 { "exit", close_uzbl },
433 { "search", search_forward_text },
434 { "search_reverse", search_reverse_text },
435 { "insert_mode", set_insert_mode },
443 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
445 for (i = 0; i < LENGTH(cmdlist); i++)
446 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
449 /* -- CORE FUNCTIONS -- */
452 free_action(gpointer act) {
453 Action *action = (Action*)act;
454 g_free(action->name);
456 g_free(action->param);
461 new_action(const gchar *name, const gchar *param) {
462 Action *action = g_new(Action, 1);
464 action->name = g_strdup(name);
466 action->param = g_strdup(param);
468 action->param = NULL;
474 file_exists (const char * filename) {
475 return (access(filename, F_OK) == 0);
479 set_insert_mode(WebKitWebView *page, const gchar *param) {
483 uzbl.behave.insert_mode = TRUE;
488 load_uri (WebKitWebView * web_view, const gchar *param) {
490 GString* newuri = g_string_new (param);
491 if (g_strrstr (param, "://") == NULL)
492 g_string_prepend (newuri, "http://");
493 /* if we do handle cookies, ask our handler for them */
494 webkit_web_view_load_uri (web_view, newuri->str);
495 g_string_free (newuri, TRUE);
500 run_js (WebKitWebView * web_view, const gchar *param) {
502 webkit_web_view_execute_script (web_view, param);
506 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
507 if ((param) && (param[0] != '\0')) {
508 uzbl.state.searchtx = g_strdup(param);
510 if (uzbl.state.searchtx != NULL) {
511 if (uzbl.state.verbose)
512 printf ("Searching: %s\n", uzbl.state.searchtx);
513 webkit_web_view_unmark_text_matches (page);
514 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
515 webkit_web_view_set_highlight_text_matches (page, TRUE);
516 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
517 g_free(uzbl.state.searchtx);
518 uzbl.state.searchtx = NULL;
523 search_forward_text (WebKitWebView *page, const char *param) {
524 search_text(page, param, TRUE);
528 search_reverse_text (WebKitWebView *page, const char *param) {
529 search_text(page, param, FALSE);
533 new_window_load_uri (const gchar * uri) {
534 GString* to_execute = g_string_new ("");
535 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
537 for (i = 0; entries[i].long_name != NULL; i++) {
538 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
539 gchar** str = (gchar**)entries[i].arg_data;
541 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
545 if (uzbl.state.verbose)
546 printf("\n%s\n", to_execute->str);
547 g_spawn_command_line_async (to_execute->str, NULL);
548 g_string_free (to_execute, TRUE);
552 close_uzbl (WebKitWebView *page, const char *param) {
558 /* --Statusbar functions-- */
560 build_progressbar_ascii(int percent) {
564 GString *bar = g_string_new("");
566 l = (double)percent*((double)width/100.);
567 l = (int)(l+.5)>=(int)l ? l+.5 : l;
569 for(i=0; i<(int)l; i++)
570 g_string_append(bar, "=");
573 g_string_append(bar, "·");
575 return g_string_free(bar, FALSE);
580 const GScannerConfig scan_config = {
583 ) /* cset_skip_characters */,
588 ) /* cset_identifier_first */,
595 ) /* cset_identifier_nth */,
596 ( "" ) /* cpair_comment_single */,
598 TRUE /* case_sensitive */,
600 FALSE /* skip_comment_multi */,
601 FALSE /* skip_comment_single */,
602 FALSE /* scan_comment_multi */,
603 TRUE /* scan_identifier */,
604 TRUE /* scan_identifier_1char */,
605 FALSE /* scan_identifier_NULL */,
606 TRUE /* scan_symbols */,
607 FALSE /* scan_binary */,
608 FALSE /* scan_octal */,
609 FALSE /* scan_float */,
610 FALSE /* scan_hex */,
611 FALSE /* scan_hex_dollar */,
612 FALSE /* scan_string_sq */,
613 FALSE /* scan_string_dq */,
614 TRUE /* numbers_2_int */,
615 FALSE /* int_2_float */,
616 FALSE /* identifier_2_string */,
617 FALSE /* char_2_token */,
618 FALSE /* symbol_2_token */,
619 TRUE /* scope_0_fallback */,
624 uzbl.scan = g_scanner_new(&scan_config);
625 while(symp->symbol_name) {
626 g_scanner_scope_add_symbol(uzbl.scan, 0,
628 GINT_TO_POINTER(symp->symbol_token));
634 expand_template(const char *template) {
635 if(!template) return NULL;
637 GTokenType token = G_TOKEN_NONE;
638 GString *ret = g_string_new("");
642 g_scanner_input_text(uzbl.scan, template, strlen(template));
643 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
644 token = g_scanner_get_next_token(uzbl.scan);
646 if(token == G_TOKEN_SYMBOL) {
647 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
650 buf = uzbl.state.uri?
651 g_markup_printf_escaped("%s", uzbl.state.uri) :
653 g_string_append(ret, buf);
657 buf = itos(uzbl.gui.sbar.load_progress);
658 g_string_append(ret, buf);
661 case SYM_LOADPRGSBAR:
662 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
663 g_string_append(ret, buf);
667 buf = uzbl.gui.main_title?
668 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
670 g_string_append(ret, buf);
673 case SYM_SELECTED_URI:
674 buf = uzbl.state.selected_url?
675 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
677 g_string_append(ret, buf);
681 buf = itos(uzbl.xwin);
683 uzbl.state.instance_name?uzbl.state.instance_name:buf);
687 buf = uzbl.state.keycmd->str?
688 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
690 g_string_append(ret, buf);
695 uzbl.behave.insert_mode?"[I]":"[C]");
699 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
703 buf = itos(WEBKIT_MAJOR_VERSION);
704 g_string_append(ret, buf);
708 buf = itos(WEBKIT_MINOR_VERSION);
709 g_string_append(ret, buf);
713 buf = itos(WEBKIT_MICRO_VERSION);
714 g_string_append(ret, buf);
718 g_string_append(ret, uzbl.state.unameinfo.sysname);
721 g_string_append(ret, uzbl.state.unameinfo.nodename);
724 g_string_append(ret, uzbl.state.unameinfo.release);
727 g_string_append(ret, uzbl.state.unameinfo.version);
730 g_string_append(ret, uzbl.state.unameinfo.machine);
733 g_string_append(ret, ARCH);
737 g_string_append(ret, uzbl.state.unameinfo.domainname);
741 g_string_append(ret, COMMIT);
747 else if(token == G_TOKEN_INT) {
748 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
749 g_string_append(ret, buf);
752 else if(token == G_TOKEN_IDENTIFIER) {
753 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
755 else if(token == G_TOKEN_CHAR) {
756 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
760 return g_string_free(ret, FALSE);
762 /* --End Statusbar functions-- */
765 sharg_append(GArray *a, const gchar *str) {
766 const gchar *s = (str ? str : "");
767 g_array_append_val(a, s);
770 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
772 run_command (const gchar *command, const guint npre, const gchar **args,
773 const gboolean sync, char **stdout) {
774 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
777 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
778 gchar *pid = itos(getpid());
779 gchar *xwin = itos(uzbl.xwin);
781 sharg_append(a, command);
782 for (i = 0; i < npre; i++) /* add n args before the default vars */
783 sharg_append(a, args[i]);
784 sharg_append(a, uzbl.state.config_file);
785 sharg_append(a, pid);
786 sharg_append(a, xwin);
787 sharg_append(a, uzbl.comm.fifo_path);
788 sharg_append(a, uzbl.comm.socket_path);
789 sharg_append(a, uzbl.state.uri);
790 sharg_append(a, uzbl.gui.main_title);
792 for (i = npre; i < g_strv_length((gchar**)args); i++)
793 sharg_append(a, args[i]);
795 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
796 NULL, NULL, stdout, NULL, NULL, &err);
797 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
798 NULL, NULL, NULL, &err);
800 if (uzbl.state.verbose) {
801 GString *s = g_string_new("spawned:");
802 for (i = 0; i < (a->len); i++) {
803 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
804 g_string_append_printf(s, " %s", qarg);
807 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
808 printf("%s\n", s->str);
809 g_string_free(s, TRUE);
812 g_printerr("error on run_command: %s\n", err->message);
817 g_array_free (a, TRUE);
822 split_quoted(const gchar* src, const gboolean unquote) {
823 /* split on unquoted space, return array of strings;
824 remove a layer of quotes and backslashes if unquote */
825 gboolean quote = FALSE;
826 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
827 GString *s = g_string_new ("");
831 for (p = src; *p != '\0'; p++) {
832 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
833 else if (*p == '\\') { g_string_append_c(s, *p++);
834 g_string_append_c(s, *p); }
835 else if ((*p == '"') && unquote) quote = !quote;
836 else if (*p == '"') { g_string_append_c(s, *p);
838 else if ((*p == ' ') && (!quote)) {
839 dup = g_strdup(s->str);
840 g_array_append_val(a, dup);
841 g_string_truncate(s, 0);
842 } else g_string_append_c(s, *p);
844 dup = g_strdup(s->str);
845 g_array_append_val(a, dup);
846 ret = (gchar**)a->data;
847 g_array_free (a, FALSE);
848 g_string_free (s, FALSE);
853 spawn(WebKitWebView *web_view, const char *param) {
855 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
856 gchar **cmd = split_quoted(param, TRUE);
857 if (cmd) run_command(cmd[0], 0, &cmd[1], FALSE, NULL);
858 g_strfreev ((gchar**)cmd);
862 spawn_sh(WebKitWebView *web_view, const char *param) {
864 if (!uzbl.behave.shell_cmd) {
865 g_printerr ("spawn_sh: shell_cmd is not set!\n");
870 gchar *spacer = g_strdup("");
871 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
872 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
873 gchar **p = split_quoted(param, TRUE);
874 for (i = 1; i < g_strv_length(cmd); i++)
875 sharg_append(a, cmd[i]);
876 sharg_append(a, p[0]); /* the first param comes right after shell_cmd;
877 the rest come after default args */
878 sharg_append(a, spacer);
879 for (i = 1; i < g_strv_length(p); i++)
880 sharg_append(a, p[i]);
881 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, a->data, FALSE, NULL);
885 g_array_free (a, FALSE);
889 parse_command(const char *cmd, const char *param) {
892 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
893 c(uzbl.gui.web_view, param);
895 fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
901 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
902 G_REGEX_OPTIMIZE, 0, NULL);
903 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
904 G_REGEX_OPTIMIZE, 0, NULL);
905 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
906 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
907 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
908 G_REGEX_OPTIMIZE, 0, NULL);
909 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
910 G_REGEX_OPTIMIZE, 0, NULL);
914 get_var_value(gchar *name) {
917 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
918 if(var_is("uri", name)
919 || var_is("status_message", name)
920 || var_is("status_format", name)
921 || var_is("status_background", name)
922 || var_is("title_format_short", name)
923 || var_is("title_format_long", name)
924 || var_is("modkey", name)
925 || var_is("load_finish_handler", name)
926 || var_is("load_start_handler", name)
927 || var_is("load_commit_handler", name)
928 || var_is("history_handler", name)
929 || var_is("download_handler", name)
930 || var_is("cookie_handler", name)
931 || var_is("fifo_dir", name)
932 || var_is("socket_dir", name)
933 || var_is("shell_cmd", name)
934 || var_is("proxy_url", name)
935 || var_is("useragent", name))
937 printf("VAR: %s VALUE: %s\n", name, (char *)*p);
938 } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
947 if(*uzbl.net.proxy_url == ' '
948 || uzbl.net.proxy_url == NULL) {
949 soup_session_remove_feature_by_type(uzbl.net.soup_session,
950 (GType) SOUP_SESSION_PROXY_URI);
953 suri = soup_uri_new(uzbl.net.proxy_url);
954 g_object_set(G_OBJECT(uzbl.net.soup_session),
955 SOUP_SESSION_PROXY_URI,
965 gtk_widget_ref(uzbl.gui.scrolled_win);
966 gtk_widget_ref(uzbl.gui.mainbar);
967 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
968 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
970 if(uzbl.behave.status_top) {
971 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
972 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
975 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
976 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
978 gtk_widget_unref(uzbl.gui.scrolled_win);
979 gtk_widget_unref(uzbl.gui.mainbar);
980 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
984 var_is(const char *x, const char *y) {
985 return (strcmp(x, y) == 0 ? TRUE : FALSE );
989 set_var_value(gchar *name, gchar *val) {
994 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
995 if(var_is("status_message", name)
996 || var_is("status_background", name)
997 || var_is("status_format", name)
998 || var_is("title_format_long", name)
999 || var_is("title_format_short", name)
1000 || var_is("load_finish_handler", name)
1001 || var_is("load_start_handler", name)
1002 || var_is("load_commit_handler", name)
1003 || var_is("history_handler", name)
1004 || var_is("download_handler", name)
1005 || var_is("cookie_handler", name)) {
1011 else if(var_is("uri", name)) {
1014 load_uri(uzbl.gui.web_view, (const gchar*)*p);
1016 else if(var_is("proxy_url", name)) {
1021 else if(var_is("fifo_dir", name)) {
1023 buf = init_fifo(val);
1024 *p = buf?buf:g_strdup("");
1026 else if(var_is("socket_dir", name)) {
1028 buf = init_socket(val);
1029 *p = buf?buf:g_strdup("");
1031 else if(var_is("modkey", name)) {
1034 *p = g_utf8_strup(val, -1);
1035 uzbl.behave.modmask = 0;
1036 for (i = 0; modkeys[i].key != NULL; i++) {
1037 if (g_strrstr(*p, modkeys[i].key))
1038 uzbl.behave.modmask |= modkeys[i].mask;
1041 else if(var_is("useragent", name)) {
1043 buf = set_useragent(val);
1044 *p = buf?buf:g_strdup("");
1046 else if(var_is("shell_cmd", name)) {
1050 /* variables that take int values */
1053 *ip = (int)strtoul(val, &endp, 10);
1055 if(var_is("show_status", name)) {
1058 else if(var_is("always_insert_mode", name)) {
1059 uzbl.behave.insert_mode =
1060 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1063 else if (var_is("max_conns", name)) {
1064 g_object_set(G_OBJECT(uzbl.net.soup_session),
1065 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1067 else if (var_is("max_conns_host", name)) {
1068 g_object_set(G_OBJECT(uzbl.net.soup_session),
1069 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1071 else if (var_is("http_debug", name)) {
1072 soup_session_remove_feature
1073 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1074 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1075 /*g_free(uzbl.net.soup_logger);*/
1077 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1078 soup_session_add_feature(uzbl.net.soup_session,
1079 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1081 else if (var_is("status_top", name)) {
1084 else if (var_is("default_font_size", name)) {
1085 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1086 g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
1088 else if (var_is("minimum_font_size", name)) {
1089 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1090 g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
1098 runcmd(WebKitWebView* page, const char *param) {
1100 parse_cmd_line(param);
1104 parse_cmd_line(const char *ctl_line) {
1108 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1109 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1110 if(tokens[0][0] == 0) {
1111 set_var_value(tokens[1], tokens[2]);
1115 printf("Error in command: %s\n", tokens[0]);
1118 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1119 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1120 if(tokens[0][0] == 0) {
1121 get_var_value(tokens[1]);
1125 printf("Error in command: %s\n", tokens[0]);
1128 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1129 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1130 if(tokens[0][0] == 0) {
1131 add_binding(tokens[1], tokens[2]);
1135 printf("Error in command: %s\n", tokens[0]);
1138 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1139 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1140 if(tokens[0][0] == 0) {
1141 parse_command(tokens[1], tokens[2]);
1145 printf("Error in command: %s\n", tokens[0]);
1147 /* KEYCMD command */
1148 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1149 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1150 if(tokens[0][0] == 0) {
1151 /* should incremental commands want each individual "keystroke"
1152 sent in a loop or the whole string in one go like now? */
1153 g_string_assign(uzbl.state.keycmd, tokens[1]);
1155 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1162 else if( (ctl_line[0] == '#')
1163 || (ctl_line[0] == ' ')
1164 || (ctl_line[0] == '\n'))
1165 ; /* ignore these lines */
1167 printf("Command not understood (%s)\n", ctl_line);
1173 build_stream_name(int type, const gchar* dir) {
1175 State *s = &uzbl.state;
1178 xwin_str = itos((int)uzbl.xwin);
1180 str = g_strdup_printf
1181 ("%s/uzbl_fifo_%s", dir,
1182 s->instance_name ? s->instance_name : xwin_str);
1183 } else if (type == SOCKET) {
1184 str = g_strdup_printf
1185 ("%s/uzbl_socket_%s", dir,
1186 s->instance_name ? s->instance_name : xwin_str );
1193 control_fifo(GIOChannel *gio, GIOCondition condition) {
1194 if (uzbl.state.verbose)
1195 printf("triggered\n");
1200 if (condition & G_IO_HUP)
1201 g_error ("Fifo: Read end of pipe died!\n");
1204 g_error ("Fifo: GIOChannel broke\n");
1206 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1207 if (ret == G_IO_STATUS_ERROR) {
1208 g_error ("Fifo: Error reading: %s\n", err->message);
1212 parse_cmd_line(ctl_line);
1219 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1220 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1221 if (unlink(uzbl.comm.fifo_path) == -1)
1222 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1223 g_free(uzbl.comm.fifo_path);
1224 uzbl.comm.fifo_path = NULL;
1227 if (*dir == ' ') { /* space unsets the variable */
1231 GIOChannel *chan = NULL;
1232 GError *error = NULL;
1233 gchar *path = build_stream_name(FIFO, dir);
1235 if (!file_exists(path)) {
1236 if (mkfifo (path, 0666) == 0) {
1237 // 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.
1238 chan = g_io_channel_new_file(path, "r+", &error);
1240 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1241 if (uzbl.state.verbose)
1242 printf ("init_fifo: created successfully as %s\n", path);
1243 uzbl.comm.fifo_path = path;
1245 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1246 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1247 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1248 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1250 /* if we got this far, there was an error; cleanup */
1251 if (error) g_error_free (error);
1257 control_stdin(GIOChannel *gio, GIOCondition condition) {
1259 gchar *ctl_line = NULL;
1262 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1263 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1266 parse_cmd_line(ctl_line);
1274 GIOChannel *chan = NULL;
1275 GError *error = NULL;
1277 chan = g_io_channel_unix_new(fileno(stdin));
1279 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1280 g_error ("Stdin: could not add watch\n");
1282 if (uzbl.state.verbose)
1283 printf ("Stdin: watch added successfully\n");
1286 g_error ("Stdin: Error while opening: %s\n", error->message);
1288 if (error) g_error_free (error);
1292 control_socket(GIOChannel *chan) {
1293 struct sockaddr_un remote;
1294 char buffer[512], *ctl_line;
1296 int sock, clientsock, n, done;
1299 sock = g_io_channel_unix_get_fd(chan);
1301 memset (buffer, 0, sizeof (buffer));
1303 t = sizeof (remote);
1304 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1308 memset (temp, 0, sizeof (temp));
1309 n = recv (clientsock, temp, 128, 0);
1311 buffer[strlen (buffer)] = '\0';
1315 strcat (buffer, temp);
1318 if (strcmp (buffer, "\n") < 0) {
1319 buffer[strlen (buffer) - 1] = '\0';
1321 buffer[strlen (buffer)] = '\0';
1324 ctl_line = g_strdup(buffer);
1325 parse_cmd_line (ctl_line);
1328 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1329 GError *error = NULL;
1332 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1333 if (ret == G_IO_STATUS_ERROR)
1334 g_error ("Error reading: %s\n", error->message);
1336 printf("Got line %s (%u bytes) \n",ctl_line, len);
1338 parse_line(ctl_line);
1346 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1347 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1348 if (unlink(uzbl.comm.socket_path) == -1)
1349 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1350 g_free(uzbl.comm.socket_path);
1351 uzbl.comm.socket_path = NULL;
1359 GIOChannel *chan = NULL;
1361 struct sockaddr_un local;
1362 gchar *path = build_stream_name(SOCKET, dir);
1364 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1366 local.sun_family = AF_UNIX;
1367 strcpy (local.sun_path, path);
1368 unlink (local.sun_path);
1370 len = strlen (local.sun_path) + sizeof (local.sun_family);
1371 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1372 if (uzbl.state.verbose)
1373 printf ("init_socket: opened in %s\n", path);
1376 if( (chan = g_io_channel_unix_new(sock)) ) {
1377 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1378 uzbl.comm.socket_path = path;
1381 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1383 /* if we got this far, there was an error; cleanup */
1390 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1391 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1393 // this function may be called very early when the templates are not set (yet), hence the checks
1395 update_title (void) {
1396 Behaviour *b = &uzbl.behave;
1399 if (b->show_status) {
1400 if (b->title_format_short) {
1401 parsed = expand_template(b->title_format_short);
1402 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1405 if (b->status_format) {
1406 parsed = expand_template(b->status_format);
1407 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1410 if (b->status_background) {
1412 gdk_color_parse (b->status_background, &color);
1413 //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)
1414 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1417 if (b->title_format_long) {
1418 parsed = expand_template(b->title_format_long);
1419 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1426 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1428 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1432 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1433 || 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)
1436 /* turn off insert mode (if always_insert_mode is not used) */
1437 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1438 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1443 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1446 if (event->keyval == GDK_Escape) {
1447 g_string_truncate(uzbl.state.keycmd, 0);
1452 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1453 if (event->keyval == GDK_Insert) {
1455 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1456 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1458 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1461 g_string_append (uzbl.state.keycmd, str);
1468 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1469 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1473 gboolean key_ret = FALSE;
1474 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1476 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1478 run_keycmd(key_ret);
1480 if (key_ret) return (!uzbl.behave.insert_mode);
1485 run_keycmd(const gboolean key_ret) {
1486 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1488 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1489 g_string_truncate(uzbl.state.keycmd, 0);
1490 parse_command(action->name, action->param);
1494 /* try if it's an incremental keycmd or one that takes args, and run it */
1495 GString* short_keys = g_string_new ("");
1496 GString* short_keys_inc = g_string_new ("");
1498 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1499 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1500 g_string_assign(short_keys_inc, short_keys->str);
1501 g_string_append_c(short_keys, '_');
1502 g_string_append_c(short_keys_inc, '*');
1504 gboolean exec_now = FALSE;
1505 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1506 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1507 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1508 if (key_ret) { /* just quit the incremental command on return */
1509 g_string_truncate(uzbl.state.keycmd, 0);
1511 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1515 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1516 GString* actionname = g_string_new ("");
1517 GString* actionparam = g_string_new ("");
1518 g_string_erase (parampart, 0, i+1);
1520 g_string_printf (actionname, action->name, parampart->str);
1522 g_string_printf (actionparam, action->param, parampart->str);
1523 parse_command(actionname->str, actionparam->str);
1524 g_string_free (actionname, TRUE);
1525 g_string_free (actionparam, TRUE);
1526 g_string_free (parampart, TRUE);
1528 g_string_truncate(uzbl.state.keycmd, 0);
1532 g_string_truncate(short_keys, short_keys->len - 1);
1534 g_string_free (short_keys, TRUE);
1535 g_string_free (short_keys_inc, TRUE);
1542 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1543 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
1545 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1546 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1548 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1549 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1550 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1551 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1552 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1553 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1554 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1555 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1556 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1557 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1558 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1560 return scrolled_window;
1567 g->mainbar = gtk_hbox_new (FALSE, 0);
1569 g->mainbar_label = gtk_label_new ("");
1570 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1571 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1572 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1573 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1574 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1579 GtkWidget* create_window () {
1580 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1581 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1582 gtk_widget_set_name (window, "Uzbl browser");
1583 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1589 run_handler (const gchar *act, const gchar *args) {
1590 char **parts = g_strsplit(act, " ", 2);
1592 else if ((g_strcmp0(parts[0], "spawn") == 0)
1593 || (g_strcmp0(parts[0], "sh") == 0)) {
1595 GString *a = g_string_new ("");
1597 spawnparts = split_quoted(parts[1], FALSE);
1598 g_string_append_printf(a, "%s", spawnparts[0]);
1599 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1600 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1601 g_string_append_printf(a, " %s", spawnparts[i]);
1602 parse_command(parts[0], a->str);
1603 g_string_free (a, TRUE);
1604 g_strfreev (spawnparts);
1606 parse_command(parts[0], parts[1]);
1611 add_binding (const gchar *key, const gchar *act) {
1612 char **parts = g_strsplit(act, " ", 2);
1619 if (uzbl.state.verbose)
1620 printf ("Binding %-10s : %s\n", key, act);
1622 action = new_action(parts[0], parts[1]);
1623 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1629 get_xdg_var (XDG_Var xdg) {
1630 const gchar* actual_value = getenv (xdg.environmental);
1631 const gchar* home = getenv ("HOME");
1633 gchar* return_value = str_replace ("~", home, actual_value);
1635 if (! actual_value || strcmp (actual_value, "") == 0) {
1636 if (xdg.default_value) {
1637 return_value = str_replace ("~", home, xdg.default_value);
1639 return_value = NULL;
1642 return return_value;
1646 find_xdg_file (int xdg_type, char* filename) {
1647 /* xdg_type = 0 => config
1648 xdg_type = 1 => data
1649 xdg_type = 2 => cache*/
1651 gchar* temporary_file = malloc (1024);
1652 gchar* temporary_string = NULL;
1656 buf = get_xdg_var (XDG[xdg_type]);
1657 strcpy (temporary_file, buf);
1658 strcat (temporary_file, filename);
1661 if (! file_exists (temporary_file) && xdg_type != 2) {
1662 buf = get_xdg_var (XDG[3 + xdg_type]);
1663 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1666 while (temporary_string && ! file_exists (temporary_file)) {
1667 strcpy (temporary_file, temporary_string);
1668 strcat (temporary_file, filename);
1669 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1673 if (file_exists (temporary_file)) {
1674 return temporary_file;
1682 State *s = &uzbl.state;
1683 Network *n = &uzbl.net;
1685 uzbl.behave.reset_command_mode = 1;
1687 if (!s->config_file) {
1688 s->config_file = find_xdg_file (0, "/uzbl/config");
1691 if (s->config_file) {
1692 GIOChannel *chan = NULL;
1693 gchar *readbuf = NULL;
1696 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1699 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1700 == G_IO_STATUS_NORMAL) {
1701 parse_cmd_line(readbuf);
1705 g_io_channel_unref (chan);
1706 if (uzbl.state.verbose)
1707 printf ("Config %s loaded\n", s->config_file);
1709 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1712 if (uzbl.state.verbose)
1713 printf ("No configuration file loaded.\n");
1715 if (!uzbl.behave.status_format)
1716 set_var_value("status_format", STATUS_DEFAULT);
1717 if (!uzbl.behave.title_format_long)
1718 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1719 if (!uzbl.behave.title_format_short)
1720 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1723 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1727 set_useragent(gchar *val) {
1732 gchar *ua = expand_template(val);
1734 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1738 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1741 if (!uzbl.behave.cookie_handler) return;
1743 gchar * stdout = NULL;
1744 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1745 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1746 gchar *action = g_strdup ("GET");
1747 SoupURI * soup_uri = soup_message_get_uri(msg);
1748 sharg_append(a, action);
1749 sharg_append(a, soup_uri->host);
1750 sharg_append(a, soup_uri->path);
1751 run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1752 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1754 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1757 g_array_free(a, TRUE);
1761 save_cookies (SoupMessage *msg, gpointer user_data){
1765 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1766 cookie = soup_cookie_to_set_cookie_header(ck->data);
1767 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1768 SoupURI * soup_uri = soup_message_get_uri(msg);
1769 gchar *action = strdup("PUT");
1770 sharg_append(a, action);
1771 sharg_append(a, soup_uri->host);
1772 sharg_append(a, soup_uri->path);
1773 sharg_append(a, cookie);
1774 run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1777 g_array_free(a, TRUE);
1784 main (int argc, char* argv[]) {
1785 gtk_init (&argc, &argv);
1786 if (!g_thread_supported ())
1787 g_thread_init (NULL);
1789 uzbl.state.executable_path = g_strdup(argv[0]);
1790 uzbl.state.selected_url = NULL;
1791 uzbl.state.searchtx = NULL;
1793 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1794 g_option_context_add_main_entries (context, entries, NULL);
1795 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1796 g_option_context_parse (context, &argc, &argv, NULL);
1797 g_option_context_free(context);
1798 /* initialize hash table */
1799 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1801 uzbl.net.soup_session = webkit_get_default_session();
1802 uzbl.state.keycmd = g_string_new("");
1804 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1805 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1806 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1807 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1809 if(uname(&uzbl.state.unameinfo) == -1)
1810 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1815 make_var_to_name_hash();
1818 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1820 uzbl.gui.scrolled_win = create_browser();
1823 /* initial packing */
1824 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1825 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1827 uzbl.gui.main_window = create_window ();
1828 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1831 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1832 gtk_widget_show_all (uzbl.gui.main_window);
1833 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1835 if (uzbl.state.verbose) {
1836 printf("Uzbl start location: %s\n", argv[0]);
1837 printf("window_id %i\n",(int) uzbl.xwin);
1838 printf("pid %i\n", getpid ());
1839 printf("name: %s\n", uzbl.state.instance_name);
1842 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1843 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1844 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1845 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1846 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1850 if (!uzbl.behave.show_status)
1851 gtk_widget_hide(uzbl.gui.mainbar);
1858 load_uri (uzbl.gui.web_view, uzbl.state.uri);
1864 return EXIT_SUCCESS;
1867 /* vi: set et ts=4: */