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 { "history_handler", (void *)&uzbl.behave.history_handler },
80 { "download_handler", (void *)&uzbl.behave.download_handler },
81 { "cookie_handler", (void *)&uzbl.behave.cookie_handler },
82 { "fifo_dir", (void *)&uzbl.behave.fifo_dir },
83 { "socket_dir", (void *)&uzbl.behave.socket_dir },
84 { "http_debug", (void *)&uzbl.behave.http_debug },
85 { "default_font_size", (void *)&uzbl.behave.default_font_size },
86 { "minimum_font_size", (void *)&uzbl.behave.minimum_font_size },
87 { "shell_cmd", (void *)&uzbl.behave.shell_cmd },
88 { "proxy_url", (void *)&uzbl.net.proxy_url },
89 { "max_conns", (void *)&uzbl.net.max_conns },
90 { "max_conns_host", (void *)&uzbl.net.max_conns_host },
91 { "useragent", (void *)&uzbl.net.useragent },
93 }, *n2v_p = var_name_to_ptr;
99 { "SHIFT", GDK_SHIFT_MASK }, // shift
100 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
101 { "CONTROL", GDK_CONTROL_MASK }, // control
102 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
103 { "MOD2", GDK_MOD2_MASK }, // 5th mod
104 { "MOD3", GDK_MOD3_MASK }, // 6th mod
105 { "MOD4", GDK_MOD4_MASK }, // 7th mod
106 { "MOD5", GDK_MOD5_MASK }, // 8th mod
107 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
108 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
109 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
110 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
111 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
112 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
113 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
114 { "META", GDK_META_MASK }, // meta (since 2.10)
118 /* construct a hash from the var_name_to_ptr array for quick access */
120 make_var_to_name_hash() {
121 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
123 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, n2v_p->ptr);
128 /* commandline arguments (set initial values for the state variables) */
129 static GOptionEntry entries[] =
131 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
132 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
133 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
134 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
135 { NULL, 0, 0, 0, NULL, NULL, NULL }
138 typedef void (*Command)(WebKitWebView*, const char *);
140 /* --- UTILITY FUNCTIONS --- */
146 snprintf(tmp, sizeof(tmp), "%i", val);
147 return g_strdup(tmp);
151 str_replace (const char* search, const char* replace, const char* string) {
152 return g_strjoinv (replace, g_strsplit (string, search, -1));
156 setup_signal(int signr, sigfunc *shandler) {
157 struct sigaction nh, oh;
159 nh.sa_handler = shandler;
160 sigemptyset(&nh.sa_mask);
163 if(sigaction(signr, &nh, &oh) < 0)
171 if (uzbl.behave.fifo_dir)
172 unlink (uzbl.comm.fifo_path);
173 if (uzbl.behave.socket_dir)
174 unlink (uzbl.comm.socket_path);
176 g_string_free(uzbl.state.keycmd, TRUE);
177 g_hash_table_destroy(uzbl.bindings);
178 g_hash_table_destroy(uzbl.behave.commands);
182 /* --- SIGNAL HANDLER --- */
185 catch_sigterm(int s) {
191 catch_sigint(int s) {
197 /* --- CALLBACKS --- */
200 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
203 (void) navigation_action;
204 (void) policy_decision;
206 const gchar* uri = webkit_network_request_get_uri (request);
207 if (uzbl.state.verbose)
208 printf("New window requested -> %s \n", uri);
209 new_window_load_uri(uri);
214 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
218 if (uzbl.state.selected_url[0]!=0) {
219 if (uzbl.state.verbose)
220 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
221 new_window_load_uri(uzbl.state.selected_url);
223 if (uzbl.state.verbose)
224 printf("New web view -> %s\n","Nothing to open, exiting");
230 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
233 if (uzbl.behave.download_handler) {
234 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
235 if (uzbl.state.verbose)
236 printf("Download -> %s\n",uri);
237 run_command(uzbl.behave.download_handler, uri, FALSE, NULL);
242 /* scroll a bar in a given direction */
244 scroll (GtkAdjustment* bar, const char *param) {
248 amount = g_ascii_strtod(param, &end);
251 fprintf(stderr, "found something after double: %s\n", end);
253 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
256 static void scroll_begin(WebKitWebView* page, const char *param) {
257 (void) page; (void) param;
258 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
261 static void scroll_end(WebKitWebView* page, const char *param) {
262 (void) page; (void) param;
263 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
264 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
267 static void scroll_vert(WebKitWebView* page, const char *param) {
269 scroll(uzbl.gui.bar_v, param);
272 static void scroll_horz(WebKitWebView* page, const char *param) {
274 scroll(uzbl.gui.bar_h, param);
279 if (!uzbl.behave.show_status) {
280 gtk_widget_hide(uzbl.gui.mainbar);
282 gtk_widget_show(uzbl.gui.mainbar);
288 toggle_status_cb (WebKitWebView* page, const char *param) {
292 if (uzbl.behave.show_status) {
293 gtk_widget_hide(uzbl.gui.mainbar);
295 gtk_widget_show(uzbl.gui.mainbar);
297 uzbl.behave.show_status = !uzbl.behave.show_status;
302 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
306 //Set selected_url state variable
307 uzbl.state.selected_url[0] = '\0';
309 strcpy (uzbl.state.selected_url, link);
315 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
319 if (uzbl.gui.main_title)
320 g_free (uzbl.gui.main_title);
321 uzbl.gui.main_title = g_strdup (title);
326 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
329 uzbl.gui.sbar.load_progress = progress;
334 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
338 if (uzbl.behave.load_finish_handler) {
339 run_command(uzbl.behave.load_finish_handler, NULL, FALSE, NULL);
344 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
347 free (uzbl.state.uri);
348 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
349 uzbl.state.uri = g_string_free (newuri, FALSE);
350 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
351 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
354 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
358 destroy_cb (GtkWidget* widget, gpointer data) {
366 if (uzbl.behave.history_handler) {
368 struct tm * timeinfo;
371 timeinfo = localtime ( &rawtime );
372 strftime (date, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
373 GString* args = g_string_new ("");
374 g_string_printf (args, "'%s'", date);
375 run_command(uzbl.behave.history_handler, args->str, FALSE, NULL);
376 g_string_free (args, TRUE);
381 /* VIEW funcs (little webkit wrappers) */
382 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
384 VIEWFUNC(reload_bypass_cache)
385 VIEWFUNC(stop_loading)
392 /* -- command to callback/function map for things we cannot attach to any signals */
395 static struct {char *name; Command command;} cmdlist[] =
397 { "back", view_go_back },
398 { "forward", view_go_forward },
399 { "scroll_vert", scroll_vert },
400 { "scroll_horz", scroll_horz },
401 { "scroll_begin", scroll_begin },
402 { "scroll_end", scroll_end },
403 { "reload", view_reload, },
404 { "reload_ign_cache", view_reload_bypass_cache},
405 { "stop", view_stop_loading, },
406 { "zoom_in", view_zoom_in, }, //Can crash (when max zoom reached?).
407 { "zoom_out", view_zoom_out, },
409 { "script", run_js },
410 { "toggle_status", toggle_status_cb },
413 { "exit", close_uzbl },
414 { "search", search_forward_text },
415 { "search_reverse", search_reverse_text },
416 { "toggle_insert_mode", toggle_insert_mode },
424 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
426 for (i = 0; i < LENGTH(cmdlist); i++)
427 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
430 /* -- CORE FUNCTIONS -- */
433 free_action(gpointer act) {
434 Action *action = (Action*)act;
435 g_free(action->name);
437 g_free(action->param);
442 new_action(const gchar *name, const gchar *param) {
443 Action *action = g_new(Action, 1);
445 action->name = g_strdup(name);
447 action->param = g_strdup(param);
449 action->param = NULL;
455 file_exists (const char * filename) {
456 return (access(filename, F_OK) == 0);
460 toggle_insert_mode(WebKitWebView *page, const gchar *param) {
465 if (strcmp (param, "0") == 0) {
466 uzbl.behave.insert_mode = FALSE;
468 uzbl.behave.insert_mode = TRUE;
471 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
478 load_uri (WebKitWebView * web_view, const gchar *param) {
480 GString* newuri = g_string_new (param);
481 if (g_strrstr (param, "://") == NULL)
482 g_string_prepend (newuri, "http://");
483 /* if we do handle cookies, ask our handler for them */
484 webkit_web_view_load_uri (web_view, newuri->str);
485 g_string_free (newuri, TRUE);
490 run_js (WebKitWebView * web_view, const gchar *param) {
492 webkit_web_view_execute_script (web_view, param);
496 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
497 if ((param) && (param[0] != '\0')) {
498 strcpy(uzbl.state.searchtx, param);
500 if (uzbl.state.searchtx[0] != '\0') {
501 if (uzbl.state.verbose)
502 printf ("Searching: %s\n", uzbl.state.searchtx);
503 webkit_web_view_unmark_text_matches (page);
504 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
505 webkit_web_view_set_highlight_text_matches (page, TRUE);
506 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
511 search_forward_text (WebKitWebView *page, const char *param) {
512 search_text(page, param, TRUE);
516 search_reverse_text (WebKitWebView *page, const char *param) {
517 search_text(page, param, FALSE);
521 new_window_load_uri (const gchar * uri) {
522 GString* to_execute = g_string_new ("");
523 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
525 for (i = 0; entries[i].long_name != NULL; i++) {
526 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
527 gchar** str = (gchar**)entries[i].arg_data;
529 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
533 if (uzbl.state.verbose)
534 printf("\n%s\n", to_execute->str);
535 g_spawn_command_line_async (to_execute->str, NULL);
536 g_string_free (to_execute, TRUE);
540 close_uzbl (WebKitWebView *page, const char *param) {
546 /* --Statusbar functions-- */
548 build_progressbar_ascii(int percent) {
552 GString *bar = g_string_new("");
554 l = (double)percent*((double)width/100.);
555 l = (int)(l+.5)>=(int)l ? l+.5 : l;
557 for(i=0; i<(int)l; i++)
558 g_string_append(bar, "=");
561 g_string_append(bar, "·");
563 return g_string_free(bar, FALSE);
568 const GScannerConfig scan_config = {
571 ) /* cset_skip_characters */,
576 ) /* cset_identifier_first */,
583 ) /* cset_identifier_nth */,
584 ( "" ) /* cpair_comment_single */,
586 TRUE /* case_sensitive */,
588 FALSE /* skip_comment_multi */,
589 FALSE /* skip_comment_single */,
590 FALSE /* scan_comment_multi */,
591 TRUE /* scan_identifier */,
592 TRUE /* scan_identifier_1char */,
593 FALSE /* scan_identifier_NULL */,
594 TRUE /* scan_symbols */,
595 FALSE /* scan_binary */,
596 FALSE /* scan_octal */,
597 FALSE /* scan_float */,
598 FALSE /* scan_hex */,
599 FALSE /* scan_hex_dollar */,
600 FALSE /* scan_string_sq */,
601 FALSE /* scan_string_dq */,
602 TRUE /* numbers_2_int */,
603 FALSE /* int_2_float */,
604 FALSE /* identifier_2_string */,
605 FALSE /* char_2_token */,
606 FALSE /* symbol_2_token */,
607 TRUE /* scope_0_fallback */,
612 uzbl.scan = g_scanner_new(&scan_config);
613 while(symp->symbol_name) {
614 g_scanner_scope_add_symbol(uzbl.scan, 0,
616 GINT_TO_POINTER(symp->symbol_token));
622 expand_template(const char *template) {
623 if(!template) return NULL;
625 GTokenType token = G_TOKEN_NONE;
626 GString *ret = g_string_new("");
630 g_scanner_input_text(uzbl.scan, template, strlen(template));
631 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
632 token = g_scanner_get_next_token(uzbl.scan);
634 if(token == G_TOKEN_SYMBOL) {
635 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
640 g_markup_printf_escaped("%s", uzbl.state.uri):"");
643 buf = itos(uzbl.gui.sbar.load_progress);
644 g_string_append(ret, buf);
647 case SYM_LOADPRGSBAR:
648 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
649 g_string_append(ret, buf);
655 g_markup_printf_escaped("%s", uzbl.gui.main_title):"");
657 case SYM_SELECTED_URI:
659 uzbl.state.selected_url?
660 g_markup_printf_escaped("%s", uzbl.state.selected_url):"");
663 buf = itos(uzbl.xwin);
665 uzbl.state.instance_name?uzbl.state.instance_name:buf);
670 uzbl.state.keycmd->str ?
671 g_markup_printf_escaped("%s", uzbl.state.keycmd->str):"");
675 uzbl.behave.insert_mode?"[I]":"[C]");
679 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
683 buf = itos(WEBKIT_MAJOR_VERSION);
684 g_string_append(ret, buf);
688 buf = itos(WEBKIT_MINOR_VERSION);
689 g_string_append(ret, buf);
693 buf = itos(WEBKIT_MICRO_VERSION);
694 g_string_append(ret, buf);
698 g_string_append(ret, uzbl.state.unameinfo.sysname);
701 g_string_append(ret, uzbl.state.unameinfo.nodename);
704 g_string_append(ret, uzbl.state.unameinfo.release);
707 g_string_append(ret, uzbl.state.unameinfo.version);
710 g_string_append(ret, uzbl.state.unameinfo.machine);
713 g_string_append(ret, ARCH);
717 g_string_append(ret, uzbl.state.unameinfo.domainname);
721 g_string_append(ret, COMMIT);
727 else if(token == G_TOKEN_INT) {
728 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
729 g_string_append(ret, buf);
732 else if(token == G_TOKEN_IDENTIFIER) {
733 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
735 else if(token == G_TOKEN_CHAR) {
736 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
740 return g_string_free(ret, FALSE);
742 /* --End Statusbar functions-- */
745 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
747 run_command (const char *command, const char *args, const gboolean sync, char **stdout) {
748 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
749 GString *to_execute = g_string_new ("");
751 gchar *cmd = g_strstrip(g_strdup(command));
752 gchar *qcfg = (uzbl.state.config_file ? g_shell_quote(uzbl.state.config_file) : g_strdup("''"));
753 gchar *qfifo = (uzbl.comm.fifo_path ? g_shell_quote(uzbl.comm.fifo_path) : g_strdup("''"));
754 gchar *qsock = (uzbl.comm.socket_path ? g_shell_quote(uzbl.comm.socket_path) : g_strdup("''"));
755 gchar *quri = (uzbl.state.uri ? g_shell_quote(uzbl.state.uri) : g_strdup("''"));
756 gchar *qtitle = (uzbl.gui.main_title ? g_shell_quote(uzbl.gui.main_title) : g_strdup("''"));
759 g_string_printf (to_execute, "%s %s '%i' '%i' %s %s",
760 cmd, qcfg, (int) getpid(), (int) uzbl.xwin, qfifo, qsock);
761 g_string_append_printf (to_execute, " %s %s", quri, qtitle);
762 if(args) g_string_append_printf (to_execute, " %s", args);
765 result = g_spawn_command_line_sync (to_execute->str, stdout, NULL, NULL, &err);
766 } else result = g_spawn_command_line_async (to_execute->str, &err);
767 if (uzbl.state.verbose)
768 printf("Called %s. Result: %s\n", to_execute->str, (result ? "TRUE" : "FALSE" ));
769 g_string_free (to_execute, TRUE);
771 g_printerr("error on run_command: %s\n", err->message);
785 spawn(WebKitWebView *web_view, const char *param) {
788 TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
789 gchar** cmd = g_strsplit(param, " ", 2);
792 args = g_shell_quote(cmd[1]);
795 run_command(cmd[0], args, FALSE, NULL);
801 run_command(param, NULL, FALSE, NULL);
805 spawn_sh(WebKitWebView *web_view, const char *param) {
807 gchar *cmd = g_strdup_printf(uzbl.behave.shell_cmd, param);
813 parse_command(const char *cmd, const char *param) {
816 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
817 c(uzbl.gui.web_view, param);
819 fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
825 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
826 G_REGEX_OPTIMIZE, 0, NULL);
827 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
828 G_REGEX_OPTIMIZE, 0, NULL);
829 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
830 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
831 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
832 G_REGEX_OPTIMIZE, 0, NULL);
833 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
834 G_REGEX_OPTIMIZE, 0, NULL);
838 get_var_value(gchar *name) {
841 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
842 if(var_is("uri", name)
843 || var_is("status_message", name)
844 || var_is("status_format", name)
845 || var_is("status_background", name)
846 || var_is("title_format_short", name)
847 || var_is("title_format_long", name)
848 || var_is("modkey", name)
849 || var_is("load_finish_handler", name)
850 || var_is("history_handler", name)
851 || var_is("download_handler", name)
852 || var_is("cookie_handler", name)
853 || var_is("fifo_dir", name)
854 || var_is("socket_dir", name)
855 || var_is("shell_cmd", name)
856 || var_is("proxy_url", name)
857 || var_is("useragent", name))
859 printf("VAR: %s VALUE: %s\n", name, (char *)*p);
860 } else printf("VAR: %s VALUE: %d\n", name, (int)*p);
869 if(*uzbl.net.proxy_url == ' '
870 || uzbl.net.proxy_url == NULL) {
871 soup_session_remove_feature_by_type(uzbl.net.soup_session,
872 (GType) SOUP_SESSION_PROXY_URI);
875 suri = soup_uri_new(uzbl.net.proxy_url);
876 g_object_set(G_OBJECT(uzbl.net.soup_session),
877 SOUP_SESSION_PROXY_URI,
887 gtk_widget_ref(uzbl.gui.scrolled_win);
888 gtk_widget_ref(uzbl.gui.mainbar);
889 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
890 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
892 if(uzbl.behave.status_top) {
893 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
894 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
897 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
898 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
900 gtk_widget_unref(uzbl.gui.scrolled_win);
901 gtk_widget_unref(uzbl.gui.mainbar);
902 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
906 var_is(const char *x, const char *y) {
907 return (strcmp(x, y) == 0 ? TRUE : FALSE );
911 set_var_value(gchar *name, gchar *val) {
915 if( (p = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
916 if(var_is("status_message", name)
917 || var_is("status_background", name)
918 || var_is("status_format", name)
919 || var_is("title_format_long", name)
920 || var_is("title_format_short", name)
921 || var_is("load_finish_handler", name)
922 || var_is("history_handler", name)
923 || var_is("download_handler", name)
924 || var_is("cookie_handler", name)) {
930 else if(var_is("uri", name)) {
933 load_uri(uzbl.gui.web_view, (const gchar*)*p);
935 else if(var_is("proxy_url", name)) {
940 else if(var_is("fifo_dir", name)) {
942 *p = init_fifo(g_strdup(val));
944 else if(var_is("socket_dir", name)) {
946 *p = init_socket(g_strdup(val));
948 else if(var_is("modkey", name)) {
951 *p = g_utf8_strup(val, -1);
952 uzbl.behave.modmask = 0;
953 for (i = 0; modkeys[i].key != NULL; i++) {
954 if (g_strrstr(*p, modkeys[i].key))
955 uzbl.behave.modmask |= modkeys[i].mask;
958 else if(var_is("useragent", name)) {
960 *p = set_useragent(g_strdup(val));
962 else if(var_is("shell_cmd", name)) {
966 /* variables that take int values */
969 *ip = (int)strtoul(val, &endp, 10);
971 if(var_is("show_status", name)) {
974 else if(var_is("always_insert_mode", name)) {
975 uzbl.behave.insert_mode =
976 uzbl.behave.always_insert_mode ? TRUE : FALSE;
979 else if (var_is("max_conns", name)) {
980 g_object_set(G_OBJECT(uzbl.net.soup_session),
981 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
983 else if (var_is("max_conns_host", name)) {
984 g_object_set(G_OBJECT(uzbl.net.soup_session),
985 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
987 else if (var_is("http_debug", name)) {
988 //soup_session_remove_feature
989 // (uzbl.net.soup_session, uzbl.net.soup_logger);
990 soup_session_remove_feature
991 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
992 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
993 /*g_free(uzbl.net.soup_logger);*/
995 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
996 soup_session_add_feature(uzbl.net.soup_session,
997 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
999 else if (var_is("status_top", name)) {
1002 else if (var_is("default_font_size", name)) {
1003 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1004 g_object_set (G_OBJECT(ws), "default-font-size", *ip, NULL);
1006 else if (var_is("minimum_font_size", name)) {
1007 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1008 g_object_set (G_OBJECT(ws), "minimum-font-size", *ip, NULL);
1016 runcmd(WebKitWebView* page, const char *param) {
1018 parse_cmd_line(param);
1022 parse_cmd_line(const char *ctl_line) {
1026 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1027 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1028 if(tokens[0][0] == 0) {
1029 set_var_value(tokens[1], tokens[2]);
1033 printf("Error in command: %s\n", tokens[0]);
1036 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1037 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1038 if(tokens[0][0] == 0) {
1039 get_var_value(tokens[1]);
1043 printf("Error in command: %s\n", tokens[0]);
1046 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1047 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1048 if(tokens[0][0] == 0) {
1049 add_binding(tokens[1], tokens[2]);
1053 printf("Error in command: %s\n", tokens[0]);
1056 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1057 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1058 if(tokens[0][0] == 0) {
1059 parse_command(tokens[1], tokens[2]);
1063 printf("Error in command: %s\n", tokens[0]);
1065 /* KEYCMD command */
1066 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1067 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1068 if(tokens[0][0] == 0) {
1069 /* should incremental commands want each individual "keystroke"
1070 sent in a loop or the whole string in one go like now? */
1071 g_string_assign(uzbl.state.keycmd, tokens[1]);
1078 else if( (ctl_line[0] == '#')
1079 || (ctl_line[0] == ' ')
1080 || (ctl_line[0] == '\n'))
1081 ; /* ignore these lines */
1083 printf("Command not understood (%s)\n", ctl_line);
1089 build_stream_name(int type, const gchar* dir) {
1091 State *s = &uzbl.state;
1094 xwin_str = itos((int)uzbl.xwin);
1096 str = g_strdup_printf
1097 ("%s/uzbl_fifo_%s", dir,
1098 s->instance_name ? s->instance_name : xwin_str);
1099 } else if (type == SOCKET) {
1100 str = g_strdup_printf
1101 ("%s/uzbl_socket_%s", dir,
1102 s->instance_name ? s->instance_name : xwin_str );
1109 control_fifo(GIOChannel *gio, GIOCondition condition) {
1110 if (uzbl.state.verbose)
1111 printf("triggered\n");
1116 if (condition & G_IO_HUP)
1117 g_error ("Fifo: Read end of pipe died!\n");
1120 g_error ("Fifo: GIOChannel broke\n");
1122 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1123 if (ret == G_IO_STATUS_ERROR) {
1124 g_error ("Fifo: Error reading: %s\n", err->message);
1128 parse_cmd_line(ctl_line);
1135 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1136 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1137 if (unlink(uzbl.comm.fifo_path) == -1)
1138 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1139 g_free(uzbl.comm.fifo_path);
1140 uzbl.comm.fifo_path = NULL;
1143 if (*dir == ' ') { /* space unsets the variable */
1148 GIOChannel *chan = NULL;
1149 GError *error = NULL;
1150 gchar *path = build_stream_name(FIFO, dir);
1152 if (!file_exists(path)) {
1153 if (mkfifo (path, 0666) == 0) {
1154 // 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.
1155 chan = g_io_channel_new_file(path, "r+", &error);
1157 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1158 if (uzbl.state.verbose)
1159 printf ("init_fifo: created successfully as %s\n", path);
1160 uzbl.comm.fifo_path = path;
1162 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1163 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1164 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1165 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1167 /* if we got this far, there was an error; cleanup */
1168 if (error) g_error_free (error);
1175 control_stdin(GIOChannel *gio, GIOCondition condition) {
1176 gchar *ctl_line = NULL;
1177 gsize ctl_line_len = 0;
1180 if (condition & G_IO_HUP) {
1181 ret = g_io_channel_shutdown (gio, FALSE, NULL);
1185 ret = g_io_channel_read_line(gio, &ctl_line, &ctl_line_len, NULL, NULL);
1186 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1189 parse_cmd_line(ctl_line);
1197 GIOChannel *chan = NULL;
1198 GError *error = NULL;
1200 chan = g_io_channel_unix_new(fileno(stdin));
1202 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1203 g_error ("Stdin: could not add watch\n");
1205 if (uzbl.state.verbose)
1206 printf ("Stdin: watch added successfully\n");
1209 g_error ("Stdin: Error while opening: %s\n", error->message);
1211 if (error) g_error_free (error);
1215 control_socket(GIOChannel *chan) {
1216 struct sockaddr_un remote;
1217 char buffer[512], *ctl_line;
1219 int sock, clientsock, n, done;
1222 sock = g_io_channel_unix_get_fd(chan);
1224 memset (buffer, 0, sizeof (buffer));
1226 t = sizeof (remote);
1227 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1231 memset (temp, 0, sizeof (temp));
1232 n = recv (clientsock, temp, 128, 0);
1234 buffer[strlen (buffer)] = '\0';
1238 strcat (buffer, temp);
1241 if (strcmp (buffer, "\n") < 0) {
1242 buffer[strlen (buffer) - 1] = '\0';
1244 buffer[strlen (buffer)] = '\0';
1247 ctl_line = g_strdup(buffer);
1248 parse_cmd_line (ctl_line);
1251 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1252 GError *error = NULL;
1255 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1256 if (ret == G_IO_STATUS_ERROR)
1257 g_error ("Error reading: %s\n", error->message);
1259 printf("Got line %s (%u bytes) \n",ctl_line, len);
1261 parse_line(ctl_line);
1269 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1270 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1271 if (unlink(uzbl.comm.socket_path) == -1)
1272 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1273 g_free(uzbl.comm.socket_path);
1274 uzbl.comm.socket_path = NULL;
1282 GIOChannel *chan = NULL;
1284 struct sockaddr_un local;
1285 gchar *path = build_stream_name(SOCKET, dir);
1287 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1289 local.sun_family = AF_UNIX;
1290 strcpy (local.sun_path, path);
1291 unlink (local.sun_path);
1293 len = strlen (local.sun_path) + sizeof (local.sun_family);
1294 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1295 if (uzbl.state.verbose)
1296 printf ("init_socket: opened in %s\n", path);
1299 if( (chan = g_io_channel_unix_new(sock)) ) {
1300 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1301 uzbl.comm.socket_path = path;
1304 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1306 /* if we got this far, there was an error; cleanup */
1313 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1314 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1316 // this function may be called very early when the templates are not set (yet), hence the checks
1318 update_title (void) {
1319 Behaviour *b = &uzbl.behave;
1322 if (b->show_status) {
1323 if (b->title_format_short) {
1324 parsed = expand_template(b->title_format_short);
1325 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1328 if (b->status_format) {
1329 parsed = expand_template(b->status_format);
1330 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1333 if (b->status_background) {
1335 gdk_color_parse (b->status_background, &color);
1336 //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)
1337 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1340 if (b->title_format_long) {
1341 parsed = expand_template(b->title_format_long);
1342 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1349 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1351 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1355 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1356 || 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)
1359 /* turn off insert mode (if always_insert_mode is not used) */
1360 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1361 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1366 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1369 if (event->keyval == GDK_Escape) {
1370 g_string_truncate(uzbl.state.keycmd, 0);
1375 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1376 if (event->keyval == GDK_Insert) {
1378 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1379 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1381 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1384 g_string_append (uzbl.state.keycmd, str);
1391 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1392 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1396 gboolean key_ret = FALSE;
1397 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1399 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1401 run_keycmd(key_ret);
1403 if (key_ret) return (!uzbl.behave.insert_mode);
1408 run_keycmd(const gboolean key_ret) {
1409 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1411 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1412 g_string_truncate(uzbl.state.keycmd, 0);
1413 parse_command(action->name, action->param);
1417 /* try if it's an incremental keycmd or one that takes args, and run it */
1418 GString* short_keys = g_string_new ("");
1419 GString* short_keys_inc = g_string_new ("");
1421 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1422 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1423 g_string_assign(short_keys_inc, short_keys->str);
1424 g_string_append_c(short_keys, '_');
1425 g_string_append_c(short_keys_inc, '*');
1427 gboolean exec_now = FALSE;
1428 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1429 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1430 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1431 if (key_ret) { /* just quit the incremental command on return */
1432 g_string_truncate(uzbl.state.keycmd, 0);
1434 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1438 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1439 GString* actionname = g_string_new ("");
1440 GString* actionparam = g_string_new ("");
1441 g_string_erase (parampart, 0, i+1);
1443 g_string_printf (actionname, action->name, parampart->str);
1445 g_string_printf (actionparam, action->param, parampart->str);
1446 parse_command(actionname->str, actionparam->str);
1447 g_string_free (actionname, TRUE);
1448 g_string_free (actionparam, TRUE);
1449 g_string_free (parampart, TRUE);
1451 g_string_truncate(uzbl.state.keycmd, 0);
1455 g_string_truncate(short_keys, short_keys->len - 1);
1457 g_string_free (short_keys, TRUE);
1458 g_string_free (short_keys_inc, TRUE);
1465 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1466 //main_window_ref = g_object_ref(scrolled_window);
1467 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
1469 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1470 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1472 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1473 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1474 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1475 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1476 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1477 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1478 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1479 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1480 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1481 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1483 return scrolled_window;
1490 g->mainbar = gtk_hbox_new (FALSE, 0);
1492 /* keep a reference to the bar so we can re-pack it at runtime*/
1493 //sbar_ref = g_object_ref(g->mainbar);
1495 g->mainbar_label = gtk_label_new ("");
1496 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1497 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1498 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1499 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1500 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1505 GtkWidget* create_window () {
1506 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1507 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1508 gtk_widget_set_name (window, "Uzbl browser");
1509 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1515 add_binding (const gchar *key, const gchar *act) {
1516 char **parts = g_strsplit(act, " ", 2);
1523 if (uzbl.state.verbose)
1524 printf ("Binding %-10s : %s\n", key, act);
1525 action = new_action(parts[0], parts[1]);
1527 if(g_hash_table_lookup(uzbl.bindings, key))
1528 g_hash_table_remove(uzbl.bindings, key);
1529 g_hash_table_insert(uzbl.bindings, g_strdup(key), action);
1535 get_xdg_var (XDG_Var xdg) {
1536 const gchar* actual_value = getenv (xdg.environmental);
1537 const gchar* home = getenv ("HOME");
1539 gchar* return_value = str_replace ("~", home, g_strdup (actual_value));
1541 if (! actual_value || strcmp (actual_value, "") == 0) {
1542 if (xdg.default_value) {
1543 return_value = str_replace ("~", home, g_strdup (xdg.default_value));
1545 return_value = NULL;
1549 return return_value;
1553 find_xdg_file (int xdg_type, char* filename) {
1554 /* xdg_type = 0 => config
1555 xdg_type = 1 => data
1556 xdg_type = 2 => cache*/
1558 gchar* temporary_file = (char *)malloc (1024);
1559 gchar* temporary_string = NULL;
1562 strcpy (temporary_file, get_xdg_var (XDG[xdg_type]));
1564 strcat (temporary_file, filename);
1566 if (! file_exists (temporary_file) && xdg_type != 2) {
1567 temporary_string = (char *) strtok_r (get_xdg_var (XDG[3 + xdg_type]), ":", &saveptr);
1569 while (temporary_string && ! file_exists (temporary_file)) {
1570 strcpy (temporary_file, temporary_string);
1571 strcat (temporary_file, filename);
1572 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1576 if (file_exists (temporary_file)) {
1577 return temporary_file;
1585 State *s = &uzbl.state;
1586 Network *n = &uzbl.net;
1588 uzbl.behave.reset_command_mode = 1;
1590 if (!s->config_file) {
1591 s->config_file = g_strdup (find_xdg_file (0, "/uzbl/config"));
1594 if (s->config_file) {
1595 GIOChannel *chan = NULL;
1596 gchar *readbuf = NULL;
1599 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1602 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1603 == G_IO_STATUS_NORMAL) {
1604 parse_cmd_line(readbuf);
1608 g_io_channel_unref (chan);
1609 if (uzbl.state.verbose)
1610 printf ("Config %s loaded\n", s->config_file);
1612 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1615 if (uzbl.state.verbose)
1616 printf ("No configuration file loaded.\n");
1618 if (!uzbl.behave.status_format)
1619 set_var_value("status_format", STATUS_DEFAULT);
1620 if (!uzbl.behave.title_format_long)
1621 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1622 if (!uzbl.behave.title_format_short)
1623 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1626 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1630 set_useragent(gchar *val) {
1635 gchar *ua = expand_template(val);
1637 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1641 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1644 if (!uzbl.behave.cookie_handler) return;
1646 gchar * stdout = NULL;
1647 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1648 GString* args = g_string_new ("");
1649 SoupURI * soup_uri = soup_message_get_uri(msg);
1650 g_string_printf (args, "GET %s %s", soup_uri->host, soup_uri->path);
1651 run_command(uzbl.behave.cookie_handler, args->str, TRUE, &stdout);
1653 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1655 g_string_free(args, TRUE);
1659 save_cookies (SoupMessage *msg, gpointer user_data){
1663 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1664 cookie = soup_cookie_to_set_cookie_header(ck->data);
1665 GString* args = g_string_new ("");
1666 SoupURI * soup_uri = soup_message_get_uri(msg);
1667 g_string_printf (args, "PUT %s %s \"%s\"", soup_uri->host, soup_uri->path, cookie);
1668 run_command(uzbl.behave.cookie_handler, args->str, FALSE, NULL);
1669 g_string_free(args, TRUE);
1677 main (int argc, char* argv[]) {
1678 gtk_init (&argc, &argv);
1679 if (!g_thread_supported ())
1680 g_thread_init (NULL);
1682 strcpy(uzbl.state.executable_path,argv[0]);
1684 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1685 g_option_context_add_main_entries (context, entries, NULL);
1686 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1687 g_option_context_parse (context, &argc, &argv, NULL);
1688 /* initialize hash table */
1689 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1691 uzbl.net.soup_session = webkit_get_default_session();
1692 uzbl.state.keycmd = g_string_new("");
1694 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1695 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1696 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1697 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1699 if(uname(&uzbl.state.unameinfo) == -1)
1700 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1705 make_var_to_name_hash();
1708 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1710 uzbl.gui.scrolled_win = create_browser();
1713 /* initial packing */
1714 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1715 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1717 uzbl.gui.main_window = create_window ();
1718 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1720 load_uri (uzbl.gui.web_view, uzbl.state.uri); //TODO: is this needed?
1722 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1723 gtk_widget_show_all (uzbl.gui.main_window);
1724 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1726 if (uzbl.state.verbose) {
1727 printf("Uzbl start location: %s\n", argv[0]);
1728 printf("window_id %i\n",(int) uzbl.xwin);
1729 printf("pid %i\n", getpid ());
1730 printf("name: %s\n", uzbl.state.instance_name);
1733 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1734 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1735 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1736 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1737 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1741 if (!uzbl.behave.show_status)
1742 gtk_widget_hide(uzbl.gui.mainbar);
1751 return EXIT_SUCCESS;
1754 /* vi: set et ts=4: */