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 */
62 typedef const struct {
71 } var_name_to_ptr[] = {
72 { "uri", {.ptr = (void *)&uzbl.state.uri, .is_string = 1}},
73 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .is_string = 1}},
74 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .is_string = 0}},
75 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .is_string = 0}},
76 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .is_string = 1}},
77 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .is_string = 1}},
78 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .is_string = 1}},
79 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .is_string = 1}},
80 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .is_string = 0}},
81 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .is_string = 0}},
82 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .is_string = 0}},
83 { "modkey" , {.ptr = (void *)&uzbl.behave.modkey, .is_string = 0}},
84 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .is_string = 1}},
85 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .is_string = 1}},
86 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .is_string = 1}},
87 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .is_string = 1}},
88 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .is_string = 1}},
89 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .is_string = 1}},
90 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .is_string = 1}},
91 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .is_string = 1}},
92 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .is_string = 0}},
93 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .is_string = 0}},
94 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .is_string = 0}},
95 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .is_string = 1}},
96 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .is_string = 1}},
97 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .is_string = 1}},
98 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .is_string = 0}},
99 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .is_string = 1}},
100 { NULL, {.ptr = NULL, .is_string = 0}}
101 }, *n2v_p = var_name_to_ptr;
107 { "SHIFT", GDK_SHIFT_MASK }, // shift
108 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
109 { "CONTROL", GDK_CONTROL_MASK }, // control
110 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
111 { "MOD2", GDK_MOD2_MASK }, // 5th mod
112 { "MOD3", GDK_MOD3_MASK }, // 6th mod
113 { "MOD4", GDK_MOD4_MASK }, // 7th mod
114 { "MOD5", GDK_MOD5_MASK }, // 8th mod
115 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
116 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
117 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
118 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
119 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
120 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
121 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
122 { "META", GDK_META_MASK }, // meta (since 2.10)
127 dump_vars(gpointer key, gpointer val, gpointer data) {
133 is_string = c->is_string;
136 printf("KEY: %s\tVAL: %s\tIS_STRING: %s\n",
137 (char *)key, (char *)*c->ptr, "YES");
139 printf("KEY: %s\tVAL: %d\tIS_STRING: %s\n",
140 (char *)key, (int)*c->ptr, "NO");
144 /* construct a hash from the var_name_to_ptr array for quick access */
146 make_var_to_name_hash() {
147 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
149 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
154 /* commandline arguments (set initial values for the state variables) */
155 static GOptionEntry entries[] =
157 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
158 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
159 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
160 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
161 { NULL, 0, 0, 0, NULL, NULL, NULL }
164 typedef void (*Command)(WebKitWebView*, const char *);
166 /* --- UTILITY FUNCTIONS --- */
172 snprintf(tmp, sizeof(tmp), "%i", val);
173 return g_strdup(tmp);
177 str_replace (const char* search, const char* replace, const char* string) {
181 buf = g_strsplit (string, search, -1);
182 ret = g_strjoinv (replace, buf);
189 setup_signal(int signr, sigfunc *shandler) {
190 struct sigaction nh, oh;
192 nh.sa_handler = shandler;
193 sigemptyset(&nh.sa_mask);
196 if(sigaction(signr, &nh, &oh) < 0)
204 if (uzbl.behave.fifo_dir)
205 unlink (uzbl.comm.fifo_path);
206 if (uzbl.behave.socket_dir)
207 unlink (uzbl.comm.socket_path);
209 g_free(uzbl.state.executable_path);
210 g_string_free(uzbl.state.keycmd, TRUE);
211 g_hash_table_destroy(uzbl.bindings);
212 g_hash_table_destroy(uzbl.behave.commands);
216 /* --- SIGNAL HANDLER --- */
219 catch_sigterm(int s) {
225 catch_sigint(int s) {
231 /* --- CALLBACKS --- */
234 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
237 (void) navigation_action;
238 (void) policy_decision;
240 const gchar* uri = webkit_network_request_get_uri (request);
241 if (uzbl.state.verbose)
242 printf("New window requested -> %s \n", uri);
243 new_window_load_uri(uri);
248 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
252 if (uzbl.state.selected_url != NULL) {
253 if (uzbl.state.verbose)
254 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
255 new_window_load_uri(uzbl.state.selected_url);
257 if (uzbl.state.verbose)
258 printf("New web view -> %s\n","Nothing to open, exiting");
264 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
267 if (uzbl.behave.download_handler) {
268 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
269 if (uzbl.state.verbose)
270 printf("Download -> %s\n",uri);
271 /* if urls not escaped, we may have to escape and quote uri before this call */
272 run_handler(uzbl.behave.download_handler, uri);
277 /* scroll a bar in a given direction */
279 scroll (GtkAdjustment* bar, const char *param) {
283 amount = g_ascii_strtod(param, &end);
284 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
285 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
288 static void scroll_begin(WebKitWebView* page, const char *param) {
289 (void) page; (void) param;
290 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
293 static void scroll_end(WebKitWebView* page, const char *param) {
294 (void) page; (void) param;
295 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
296 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
299 static void scroll_vert(WebKitWebView* page, const char *param) {
301 scroll(uzbl.gui.bar_v, param);
304 static void scroll_horz(WebKitWebView* page, const char *param) {
306 scroll(uzbl.gui.bar_h, param);
311 if (!uzbl.behave.show_status) {
312 gtk_widget_hide(uzbl.gui.mainbar);
314 gtk_widget_show(uzbl.gui.mainbar);
320 toggle_status_cb (WebKitWebView* page, const char *param) {
324 if (uzbl.behave.show_status) {
325 gtk_widget_hide(uzbl.gui.mainbar);
327 gtk_widget_show(uzbl.gui.mainbar);
329 uzbl.behave.show_status = !uzbl.behave.show_status;
334 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
338 //Set selected_url state variable
339 g_free(uzbl.state.selected_url);
340 uzbl.state.selected_url = NULL;
342 uzbl.state.selected_url = g_strdup(link);
348 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
352 if (uzbl.gui.main_title)
353 g_free (uzbl.gui.main_title);
354 uzbl.gui.main_title = g_strdup (title);
359 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
362 uzbl.gui.sbar.load_progress = progress;
367 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
371 if (uzbl.behave.load_finish_handler)
372 run_handler(uzbl.behave.load_finish_handler, "");
376 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
380 if (uzbl.behave.load_start_handler)
381 run_handler(uzbl.behave.load_start_handler, "");
385 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
388 free (uzbl.state.uri);
389 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
390 uzbl.state.uri = g_string_free (newuri, FALSE);
391 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
392 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
395 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
396 if (uzbl.behave.load_commit_handler)
397 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
401 destroy_cb (GtkWidget* widget, gpointer data) {
409 if (uzbl.behave.history_handler) {
411 struct tm * timeinfo;
414 timeinfo = localtime ( &rawtime );
415 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
416 run_handler(uzbl.behave.history_handler, date);
421 /* VIEW funcs (little webkit wrappers) */
422 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
424 VIEWFUNC(reload_bypass_cache)
425 VIEWFUNC(stop_loading)
432 /* -- command to callback/function map for things we cannot attach to any signals */
435 static struct {char *name; Command command;} cmdlist[] =
437 { "back", view_go_back },
438 { "forward", view_go_forward },
439 { "scroll_vert", scroll_vert },
440 { "scroll_horz", scroll_horz },
441 { "scroll_begin", scroll_begin },
442 { "scroll_end", scroll_end },
443 { "reload", view_reload, },
444 { "reload_ign_cache", view_reload_bypass_cache},
445 { "stop", view_stop_loading, },
446 { "zoom_in", view_zoom_in, }, //Can crash (when max zoom reached?).
447 { "zoom_out", view_zoom_out, },
449 { "script", run_js },
450 { "toggle_status", toggle_status_cb },
453 { "exit", close_uzbl },
454 { "search", search_forward_text },
455 { "search_reverse", search_reverse_text },
456 { "insert_mode", set_insert_mode },
464 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
466 for (i = 0; i < LENGTH(cmdlist); i++)
467 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
470 /* -- CORE FUNCTIONS -- */
473 free_action(gpointer act) {
474 Action *action = (Action*)act;
475 g_free(action->name);
477 g_free(action->param);
482 new_action(const gchar *name, const gchar *param) {
483 Action *action = g_new(Action, 1);
485 action->name = g_strdup(name);
487 action->param = g_strdup(param);
489 action->param = NULL;
495 file_exists (const char * filename) {
496 return (access(filename, F_OK) == 0);
500 set_insert_mode(WebKitWebView *page, const gchar *param) {
504 uzbl.behave.insert_mode = TRUE;
509 load_uri (WebKitWebView * web_view, const gchar *param) {
511 GString* newuri = g_string_new (param);
512 if (g_strrstr (param, "://") == NULL)
513 g_string_prepend (newuri, "http://");
514 /* if we do handle cookies, ask our handler for them */
515 webkit_web_view_load_uri (web_view, newuri->str);
516 g_string_free (newuri, TRUE);
521 run_js (WebKitWebView * web_view, const gchar *param) {
523 webkit_web_view_execute_script (web_view, param);
527 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
528 if ((param) && (param[0] != '\0')) {
529 uzbl.state.searchtx = g_strdup(param);
531 if (uzbl.state.searchtx != NULL) {
532 if (uzbl.state.verbose)
533 printf ("Searching: %s\n", uzbl.state.searchtx);
534 webkit_web_view_unmark_text_matches (page);
535 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
536 webkit_web_view_set_highlight_text_matches (page, TRUE);
537 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
538 g_free(uzbl.state.searchtx);
539 uzbl.state.searchtx = NULL;
544 search_forward_text (WebKitWebView *page, const char *param) {
545 search_text(page, param, TRUE);
549 search_reverse_text (WebKitWebView *page, const char *param) {
550 search_text(page, param, FALSE);
554 new_window_load_uri (const gchar * uri) {
555 GString* to_execute = g_string_new ("");
556 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
558 for (i = 0; entries[i].long_name != NULL; i++) {
559 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
560 gchar** str = (gchar**)entries[i].arg_data;
562 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
566 if (uzbl.state.verbose)
567 printf("\n%s\n", to_execute->str);
568 g_spawn_command_line_async (to_execute->str, NULL);
569 g_string_free (to_execute, TRUE);
573 close_uzbl (WebKitWebView *page, const char *param) {
579 /* --Statusbar functions-- */
581 build_progressbar_ascii(int percent) {
585 GString *bar = g_string_new("");
587 l = (double)percent*((double)width/100.);
588 l = (int)(l+.5)>=(int)l ? l+.5 : l;
590 for(i=0; i<(int)l; i++)
591 g_string_append(bar, "=");
594 g_string_append(bar, "·");
596 return g_string_free(bar, FALSE);
601 const GScannerConfig scan_config = {
604 ) /* cset_skip_characters */,
609 ) /* cset_identifier_first */,
616 ) /* cset_identifier_nth */,
617 ( "" ) /* cpair_comment_single */,
619 TRUE /* case_sensitive */,
621 FALSE /* skip_comment_multi */,
622 FALSE /* skip_comment_single */,
623 FALSE /* scan_comment_multi */,
624 TRUE /* scan_identifier */,
625 TRUE /* scan_identifier_1char */,
626 FALSE /* scan_identifier_NULL */,
627 TRUE /* scan_symbols */,
628 FALSE /* scan_binary */,
629 FALSE /* scan_octal */,
630 FALSE /* scan_float */,
631 FALSE /* scan_hex */,
632 FALSE /* scan_hex_dollar */,
633 FALSE /* scan_string_sq */,
634 FALSE /* scan_string_dq */,
635 TRUE /* numbers_2_int */,
636 FALSE /* int_2_float */,
637 FALSE /* identifier_2_string */,
638 FALSE /* char_2_token */,
639 FALSE /* symbol_2_token */,
640 TRUE /* scope_0_fallback */,
645 uzbl.scan = g_scanner_new(&scan_config);
646 while(symp->symbol_name) {
647 g_scanner_scope_add_symbol(uzbl.scan, 0,
649 GINT_TO_POINTER(symp->symbol_token));
655 expand_template(const char *template) {
656 if(!template) return NULL;
658 GTokenType token = G_TOKEN_NONE;
659 GString *ret = g_string_new("");
663 g_scanner_input_text(uzbl.scan, template, strlen(template));
664 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
665 token = g_scanner_get_next_token(uzbl.scan);
667 if(token == G_TOKEN_SYMBOL) {
668 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
671 buf = uzbl.state.uri?
672 g_markup_printf_escaped("%s", uzbl.state.uri) :
674 g_string_append(ret, buf);
678 buf = itos(uzbl.gui.sbar.load_progress);
679 g_string_append(ret, buf);
682 case SYM_LOADPRGSBAR:
683 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
684 g_string_append(ret, buf);
688 buf = uzbl.gui.main_title?
689 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
691 g_string_append(ret, buf);
694 case SYM_SELECTED_URI:
695 buf = uzbl.state.selected_url?
696 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
698 g_string_append(ret, buf);
702 buf = itos(uzbl.xwin);
704 uzbl.state.instance_name?uzbl.state.instance_name:buf);
708 buf = uzbl.state.keycmd->str?
709 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
711 g_string_append(ret, buf);
716 uzbl.behave.insert_mode?"[I]":"[C]");
720 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
724 buf = itos(WEBKIT_MAJOR_VERSION);
725 g_string_append(ret, buf);
729 buf = itos(WEBKIT_MINOR_VERSION);
730 g_string_append(ret, buf);
734 buf = itos(WEBKIT_MICRO_VERSION);
735 g_string_append(ret, buf);
739 g_string_append(ret, uzbl.state.unameinfo.sysname);
742 g_string_append(ret, uzbl.state.unameinfo.nodename);
745 g_string_append(ret, uzbl.state.unameinfo.release);
748 g_string_append(ret, uzbl.state.unameinfo.version);
751 g_string_append(ret, uzbl.state.unameinfo.machine);
754 g_string_append(ret, ARCH);
758 g_string_append(ret, uzbl.state.unameinfo.domainname);
762 g_string_append(ret, COMMIT);
768 else if(token == G_TOKEN_INT) {
769 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
770 g_string_append(ret, buf);
773 else if(token == G_TOKEN_IDENTIFIER) {
774 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
776 else if(token == G_TOKEN_CHAR) {
777 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
781 return g_string_free(ret, FALSE);
783 /* --End Statusbar functions-- */
786 sharg_append(GArray *a, const gchar *str) {
787 const gchar *s = (str ? str : "");
788 g_array_append_val(a, s);
791 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
793 run_command (const gchar *command, const guint npre, const gchar **args,
794 const gboolean sync, char **stdout) {
795 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
798 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
799 gchar *pid = itos(getpid());
800 gchar *xwin = itos(uzbl.xwin);
802 sharg_append(a, command);
803 for (i = 0; i < npre; i++) /* add n args before the default vars */
804 sharg_append(a, args[i]);
805 sharg_append(a, uzbl.state.config_file);
806 sharg_append(a, pid);
807 sharg_append(a, xwin);
808 sharg_append(a, uzbl.comm.fifo_path);
809 sharg_append(a, uzbl.comm.socket_path);
810 sharg_append(a, uzbl.state.uri);
811 sharg_append(a, uzbl.gui.main_title);
813 for (i = npre; i < g_strv_length((gchar**)args); i++)
814 sharg_append(a, args[i]);
816 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
817 NULL, NULL, stdout, NULL, NULL, &err);
818 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
819 NULL, NULL, NULL, &err);
821 if (uzbl.state.verbose) {
822 GString *s = g_string_new("spawned:");
823 for (i = 0; i < (a->len); i++) {
824 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
825 g_string_append_printf(s, " %s", qarg);
828 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
829 printf("%s\n", s->str);
830 g_string_free(s, TRUE);
833 g_printerr("error on run_command: %s\n", err->message);
838 g_array_free (a, TRUE);
843 split_quoted(const gchar* src, const gboolean unquote) {
844 /* split on unquoted space, return array of strings;
845 remove a layer of quotes and backslashes if unquote */
848 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
849 GString *s = g_string_new ("");
853 for (p = src; *p != '\0'; p++) {
854 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
855 else if (*p == '\\') { g_string_append_c(s, *p++);
856 g_string_append_c(s, *p); }
857 else if ((*p == '"') && unquote && !sq) dq = !dq;
858 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
860 else if ((*p == '\'') && unquote && !dq) sq = !sq;
861 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
863 else if ((*p == ' ') && !dq && !sq) {
864 dup = g_strdup(s->str);
865 g_array_append_val(a, dup);
866 g_string_truncate(s, 0);
867 } else g_string_append_c(s, *p);
869 dup = g_strdup(s->str);
870 g_array_append_val(a, dup);
871 ret = (gchar**)a->data;
872 g_array_free (a, FALSE);
873 g_string_free (s, FALSE);
878 spawn(WebKitWebView *web_view, const char *param) {
880 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
881 gchar **cmd = split_quoted(param, TRUE);
882 if (cmd) run_command(cmd[0], 0, &cmd[1], FALSE, NULL);
883 g_strfreev ((gchar**)cmd);
887 spawn_sh(WebKitWebView *web_view, const char *param) {
889 if (!uzbl.behave.shell_cmd) {
890 g_printerr ("spawn_sh: shell_cmd is not set!\n");
895 gchar *spacer = g_strdup("");
896 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
897 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
898 gchar **p = split_quoted(param, TRUE);
899 for (i = 1; i < g_strv_length(cmd); i++)
900 sharg_append(a, cmd[i]);
901 sharg_append(a, p[0]); /* the first param comes right after shell_cmd;
902 the rest come after default args */
903 sharg_append(a, spacer);
904 for (i = 1; i < g_strv_length(p); i++)
905 sharg_append(a, p[i]);
906 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, a->data, FALSE, NULL);
910 g_array_free (a, FALSE);
914 parse_command(const char *cmd, const char *param) {
917 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
918 c(uzbl.gui.web_view, param);
920 fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
926 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
927 G_REGEX_OPTIMIZE, 0, NULL);
928 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
929 G_REGEX_OPTIMIZE, 0, NULL);
930 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
931 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
932 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
933 G_REGEX_OPTIMIZE, 0, NULL);
934 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
935 G_REGEX_OPTIMIZE, 0, NULL);
939 get_var_value(gchar *name) {
942 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
944 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
946 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
955 if(*uzbl.net.proxy_url == ' '
956 || uzbl.net.proxy_url == NULL) {
957 soup_session_remove_feature_by_type(uzbl.net.soup_session,
958 (GType) SOUP_SESSION_PROXY_URI);
961 suri = soup_uri_new(uzbl.net.proxy_url);
962 g_object_set(G_OBJECT(uzbl.net.soup_session),
963 SOUP_SESSION_PROXY_URI,
973 gtk_widget_ref(uzbl.gui.scrolled_win);
974 gtk_widget_ref(uzbl.gui.mainbar);
975 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
976 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
978 if(uzbl.behave.status_top) {
979 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
980 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
983 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
984 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
986 gtk_widget_unref(uzbl.gui.scrolled_win);
987 gtk_widget_unref(uzbl.gui.mainbar);
988 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
992 var_is(const char *x, const char *y) {
993 return (strcmp(x, y) == 0 ? TRUE : FALSE );
997 set_var_value(gchar *name, gchar *val) {
999 uzbl_cmdprop *c = NULL;
1003 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1006 *c->ptr = g_strdup(val);
1008 *c->ptr = (int)strtoul(val, &endp, 10);
1011 if(var_is("status_message", name)
1012 || var_is("status_background", name)
1013 || var_is("status_format", name)
1014 || var_is("title_format_long", name)
1015 || var_is("title_format_short", name)
1016 || var_is("load_finish_handler", name)
1017 || var_is("load_start_handler", name)
1018 || var_is("load_commit_handler", name)
1019 || var_is("history_handler", name)
1020 || var_is("download_handler", name)
1021 || var_is("cookie_handler", name)) {
1024 else if(var_is("uri", name)) {
1025 load_uri(uzbl.gui.web_view, uzbl.state.uri);
1027 else if(var_is("proxy_url", name)) {
1030 else if(var_is("fifo_dir", name)) {
1032 buf = init_fifo(val);
1033 p = buf?buf:g_strdup("");
1035 else if(var_is("socket_dir", name)) {
1037 buf = init_socket(val);
1038 p = buf?buf:g_strdup("");
1040 else if(var_is("modkey", name)) {
1043 p = g_utf8_strup(val, -1);
1044 uzbl.behave.modmask = 0;
1045 for (i = 0; modkeys[i].key != NULL; i++) {
1046 if (g_strrstr(p, modkeys[i].key))
1047 uzbl.behave.modmask |= modkeys[i].mask;
1050 else if(var_is("useragent", name)) {
1052 buf = set_useragent(val);
1053 p = buf?buf:g_strdup("");
1055 else 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", uzbl.behave.default_font_size, 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", uzbl.behave.minimum_font_size, NULL);
1097 runcmd(WebKitWebView* page, const char *param) {
1099 parse_cmd_line(param);
1103 parse_cmd_line(const char *ctl_line) {
1107 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1108 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1109 if(tokens[0][0] == 0) {
1110 set_var_value(tokens[1], tokens[2]);
1114 printf("Error in command: %s\n", tokens[0]);
1117 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1118 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1119 if(tokens[0][0] == 0) {
1120 get_var_value(tokens[1]);
1124 printf("Error in command: %s\n", tokens[0]);
1127 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1128 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1129 if(tokens[0][0] == 0) {
1130 add_binding(tokens[1], tokens[2]);
1134 printf("Error in command: %s\n", tokens[0]);
1137 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1138 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1139 if(tokens[0][0] == 0) {
1140 parse_command(tokens[1], tokens[2]);
1144 printf("Error in command: %s\n", tokens[0]);
1146 /* KEYCMD command */
1147 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1148 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1149 if(tokens[0][0] == 0) {
1150 /* should incremental commands want each individual "keystroke"
1151 sent in a loop or the whole string in one go like now? */
1152 g_string_assign(uzbl.state.keycmd, tokens[1]);
1154 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1161 else if( (ctl_line[0] == '#')
1162 || (ctl_line[0] == ' ')
1163 || (ctl_line[0] == '\n'))
1164 ; /* ignore these lines */
1166 printf("Command not understood (%s)\n", ctl_line);
1172 build_stream_name(int type, const gchar* dir) {
1174 State *s = &uzbl.state;
1177 xwin_str = itos((int)uzbl.xwin);
1179 str = g_strdup_printf
1180 ("%s/uzbl_fifo_%s", dir,
1181 s->instance_name ? s->instance_name : xwin_str);
1182 } else if (type == SOCKET) {
1183 str = g_strdup_printf
1184 ("%s/uzbl_socket_%s", dir,
1185 s->instance_name ? s->instance_name : xwin_str );
1192 control_fifo(GIOChannel *gio, GIOCondition condition) {
1193 if (uzbl.state.verbose)
1194 printf("triggered\n");
1199 if (condition & G_IO_HUP)
1200 g_error ("Fifo: Read end of pipe died!\n");
1203 g_error ("Fifo: GIOChannel broke\n");
1205 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1206 if (ret == G_IO_STATUS_ERROR) {
1207 g_error ("Fifo: Error reading: %s\n", err->message);
1211 parse_cmd_line(ctl_line);
1218 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1219 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1220 if (unlink(uzbl.comm.fifo_path) == -1)
1221 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1222 g_free(uzbl.comm.fifo_path);
1223 uzbl.comm.fifo_path = NULL;
1226 if (*dir == ' ') { /* space unsets the variable */
1230 GIOChannel *chan = NULL;
1231 GError *error = NULL;
1232 gchar *path = build_stream_name(FIFO, dir);
1234 if (!file_exists(path)) {
1235 if (mkfifo (path, 0666) == 0) {
1236 // 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.
1237 chan = g_io_channel_new_file(path, "r+", &error);
1239 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1240 if (uzbl.state.verbose)
1241 printf ("init_fifo: created successfully as %s\n", path);
1242 uzbl.comm.fifo_path = path;
1244 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1245 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1246 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1247 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1249 /* if we got this far, there was an error; cleanup */
1250 if (error) g_error_free (error);
1256 control_stdin(GIOChannel *gio, GIOCondition condition) {
1258 gchar *ctl_line = NULL;
1261 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1262 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1265 parse_cmd_line(ctl_line);
1273 GIOChannel *chan = NULL;
1274 GError *error = NULL;
1276 chan = g_io_channel_unix_new(fileno(stdin));
1278 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1279 g_error ("Stdin: could not add watch\n");
1281 if (uzbl.state.verbose)
1282 printf ("Stdin: watch added successfully\n");
1285 g_error ("Stdin: Error while opening: %s\n", error->message);
1287 if (error) g_error_free (error);
1291 control_socket(GIOChannel *chan) {
1292 struct sockaddr_un remote;
1293 char buffer[512], *ctl_line;
1295 int sock, clientsock, n, done;
1298 sock = g_io_channel_unix_get_fd(chan);
1300 memset (buffer, 0, sizeof (buffer));
1302 t = sizeof (remote);
1303 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1307 memset (temp, 0, sizeof (temp));
1308 n = recv (clientsock, temp, 128, 0);
1310 buffer[strlen (buffer)] = '\0';
1314 strcat (buffer, temp);
1317 if (strcmp (buffer, "\n") < 0) {
1318 buffer[strlen (buffer) - 1] = '\0';
1320 buffer[strlen (buffer)] = '\0';
1323 ctl_line = g_strdup(buffer);
1324 parse_cmd_line (ctl_line);
1327 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1328 GError *error = NULL;
1331 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1332 if (ret == G_IO_STATUS_ERROR)
1333 g_error ("Error reading: %s\n", error->message);
1335 printf("Got line %s (%u bytes) \n",ctl_line, len);
1337 parse_line(ctl_line);
1345 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1346 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1347 if (unlink(uzbl.comm.socket_path) == -1)
1348 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1349 g_free(uzbl.comm.socket_path);
1350 uzbl.comm.socket_path = NULL;
1358 GIOChannel *chan = NULL;
1360 struct sockaddr_un local;
1361 gchar *path = build_stream_name(SOCKET, dir);
1363 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1365 local.sun_family = AF_UNIX;
1366 strcpy (local.sun_path, path);
1367 unlink (local.sun_path);
1369 len = strlen (local.sun_path) + sizeof (local.sun_family);
1370 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1371 if (uzbl.state.verbose)
1372 printf ("init_socket: opened in %s\n", path);
1375 if( (chan = g_io_channel_unix_new(sock)) ) {
1376 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1377 uzbl.comm.socket_path = path;
1380 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1382 /* if we got this far, there was an error; cleanup */
1389 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1390 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1392 // this function may be called very early when the templates are not set (yet), hence the checks
1394 update_title (void) {
1395 Behaviour *b = &uzbl.behave;
1398 if (b->show_status) {
1399 if (b->title_format_short) {
1400 parsed = expand_template(b->title_format_short);
1401 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1404 if (b->status_format) {
1405 parsed = expand_template(b->status_format);
1406 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1409 if (b->status_background) {
1411 gdk_color_parse (b->status_background, &color);
1412 //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)
1413 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1416 if (b->title_format_long) {
1417 parsed = expand_template(b->title_format_long);
1418 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1425 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1427 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1431 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1432 || 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)
1435 /* turn off insert mode (if always_insert_mode is not used) */
1436 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1437 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1442 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1445 if (event->keyval == GDK_Escape) {
1446 g_string_truncate(uzbl.state.keycmd, 0);
1451 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1452 if (event->keyval == GDK_Insert) {
1454 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1455 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1457 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1460 g_string_append (uzbl.state.keycmd, str);
1467 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1468 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1472 gboolean key_ret = FALSE;
1473 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1475 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1477 run_keycmd(key_ret);
1479 if (key_ret) return (!uzbl.behave.insert_mode);
1484 run_keycmd(const gboolean key_ret) {
1485 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1487 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1488 g_string_truncate(uzbl.state.keycmd, 0);
1489 parse_command(action->name, action->param);
1493 /* try if it's an incremental keycmd or one that takes args, and run it */
1494 GString* short_keys = g_string_new ("");
1495 GString* short_keys_inc = g_string_new ("");
1497 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1498 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1499 g_string_assign(short_keys_inc, short_keys->str);
1500 g_string_append_c(short_keys, '_');
1501 g_string_append_c(short_keys_inc, '*');
1503 gboolean exec_now = FALSE;
1504 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1505 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1506 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1507 if (key_ret) { /* just quit the incremental command on return */
1508 g_string_truncate(uzbl.state.keycmd, 0);
1510 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1514 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1515 GString* actionname = g_string_new ("");
1516 GString* actionparam = g_string_new ("");
1517 g_string_erase (parampart, 0, i+1);
1519 g_string_printf (actionname, action->name, parampart->str);
1521 g_string_printf (actionparam, action->param, parampart->str);
1522 parse_command(actionname->str, actionparam->str);
1523 g_string_free (actionname, TRUE);
1524 g_string_free (actionparam, TRUE);
1525 g_string_free (parampart, TRUE);
1527 g_string_truncate(uzbl.state.keycmd, 0);
1531 g_string_truncate(short_keys, short_keys->len - 1);
1533 g_string_free (short_keys, TRUE);
1534 g_string_free (short_keys_inc, TRUE);
1541 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1542 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
1544 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1545 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1547 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1548 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1549 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1550 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1551 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1552 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1553 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1554 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1555 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1556 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1557 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1559 return scrolled_window;
1566 g->mainbar = gtk_hbox_new (FALSE, 0);
1568 g->mainbar_label = gtk_label_new ("");
1569 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1570 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1571 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1572 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1573 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1578 GtkWidget* create_window () {
1579 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1580 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1581 gtk_widget_set_name (window, "Uzbl browser");
1582 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1588 run_handler (const gchar *act, const gchar *args) {
1589 char **parts = g_strsplit(act, " ", 2);
1591 else if ((g_strcmp0(parts[0], "spawn") == 0)
1592 || (g_strcmp0(parts[0], "sh") == 0)) {
1594 GString *a = g_string_new ("");
1596 spawnparts = split_quoted(parts[1], FALSE);
1597 g_string_append_printf(a, "%s", spawnparts[0]);
1598 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1599 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1600 g_string_append_printf(a, " %s", spawnparts[i]);
1601 parse_command(parts[0], a->str);
1602 g_string_free (a, TRUE);
1603 g_strfreev (spawnparts);
1605 parse_command(parts[0], parts[1]);
1610 add_binding (const gchar *key, const gchar *act) {
1611 char **parts = g_strsplit(act, " ", 2);
1618 if (uzbl.state.verbose)
1619 printf ("Binding %-10s : %s\n", key, act);
1621 action = new_action(parts[0], parts[1]);
1622 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1628 get_xdg_var (XDG_Var xdg) {
1629 const gchar* actual_value = getenv (xdg.environmental);
1630 const gchar* home = getenv ("HOME");
1632 gchar* return_value = str_replace ("~", home, actual_value);
1634 if (! actual_value || strcmp (actual_value, "") == 0) {
1635 if (xdg.default_value) {
1636 return_value = str_replace ("~", home, xdg.default_value);
1638 return_value = NULL;
1641 return return_value;
1645 find_xdg_file (int xdg_type, char* filename) {
1646 /* xdg_type = 0 => config
1647 xdg_type = 1 => data
1648 xdg_type = 2 => cache*/
1650 gchar* temporary_file = malloc (1024);
1651 gchar* temporary_string = NULL;
1655 buf = get_xdg_var (XDG[xdg_type]);
1656 strcpy (temporary_file, buf);
1657 strcat (temporary_file, filename);
1660 if (! file_exists (temporary_file) && xdg_type != 2) {
1661 buf = get_xdg_var (XDG[3 + xdg_type]);
1662 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1665 while (temporary_string && ! file_exists (temporary_file)) {
1666 strcpy (temporary_file, temporary_string);
1667 strcat (temporary_file, filename);
1668 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1672 if (file_exists (temporary_file)) {
1673 return temporary_file;
1681 State *s = &uzbl.state;
1682 Network *n = &uzbl.net;
1684 uzbl.behave.reset_command_mode = 1;
1686 if (!s->config_file) {
1687 s->config_file = find_xdg_file (0, "/uzbl/config");
1690 if (s->config_file) {
1691 GIOChannel *chan = NULL;
1692 gchar *readbuf = NULL;
1695 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1698 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1699 == G_IO_STATUS_NORMAL) {
1700 parse_cmd_line(readbuf);
1704 g_io_channel_unref (chan);
1705 if (uzbl.state.verbose)
1706 printf ("Config %s loaded\n", s->config_file);
1708 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1711 if (uzbl.state.verbose)
1712 printf ("No configuration file loaded.\n");
1714 if (!uzbl.behave.status_format)
1715 set_var_value("status_format", STATUS_DEFAULT);
1716 if (!uzbl.behave.title_format_long)
1717 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1718 if (!uzbl.behave.title_format_short)
1719 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1722 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1726 set_useragent(gchar *val) {
1731 gchar *ua = expand_template(val);
1733 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1737 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1740 if (!uzbl.behave.cookie_handler) return;
1742 gchar * stdout = NULL;
1743 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1744 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1745 gchar *action = g_strdup ("GET");
1746 SoupURI * soup_uri = soup_message_get_uri(msg);
1747 sharg_append(a, action);
1748 sharg_append(a, soup_uri->host);
1749 sharg_append(a, soup_uri->path);
1750 run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1751 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1753 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1756 g_array_free(a, TRUE);
1760 save_cookies (SoupMessage *msg, gpointer user_data){
1764 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1765 cookie = soup_cookie_to_set_cookie_header(ck->data);
1766 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1767 SoupURI * soup_uri = soup_message_get_uri(msg);
1768 gchar *action = strdup("PUT");
1769 sharg_append(a, action);
1770 sharg_append(a, soup_uri->host);
1771 sharg_append(a, soup_uri->path);
1772 sharg_append(a, cookie);
1773 run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1776 g_array_free(a, TRUE);
1783 main (int argc, char* argv[]) {
1784 gtk_init (&argc, &argv);
1785 if (!g_thread_supported ())
1786 g_thread_init (NULL);
1788 uzbl.state.executable_path = g_strdup(argv[0]);
1789 uzbl.state.selected_url = NULL;
1790 uzbl.state.searchtx = NULL;
1792 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1793 g_option_context_add_main_entries (context, entries, NULL);
1794 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1795 g_option_context_parse (context, &argc, &argv, NULL);
1796 g_option_context_free(context);
1797 /* initialize hash table */
1798 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1800 uzbl.net.soup_session = webkit_get_default_session();
1801 uzbl.state.keycmd = g_string_new("");
1803 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1804 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1805 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1806 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1808 if(uname(&uzbl.state.unameinfo) == -1)
1809 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1814 make_var_to_name_hash();
1817 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1819 uzbl.gui.scrolled_win = create_browser();
1822 /* initial packing */
1823 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1824 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1826 uzbl.gui.main_window = create_window ();
1827 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1830 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1831 gtk_widget_show_all (uzbl.gui.main_window);
1832 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1834 if (uzbl.state.verbose) {
1835 printf("Uzbl start location: %s\n", argv[0]);
1836 printf("window_id %i\n",(int) uzbl.xwin);
1837 printf("pid %i\n", getpid ());
1838 printf("name: %s\n", uzbl.state.instance_name);
1841 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1842 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1843 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1844 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1845 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1849 if (!uzbl.behave.show_status)
1850 gtk_widget_hide(uzbl.gui.mainbar);
1857 load_uri (uzbl.gui.web_view, uzbl.state.uri);
1862 return EXIT_SUCCESS;
1865 /* vi: set et ts=4: */