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 /* variable name pointer to variable in code string variable callback function */
73 /* ------------------------------------------------------------------------------------------------------------------- */
74 { "uri", {.ptr = (void *)&uzbl.state.uri, .is_string = 1, .func = cmd_load_uri}},
75 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .is_string = 1, .func = update_title}},
76 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .is_string = 0, .func = cmd_set_status}},
77 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .is_string = 0, .func = move_statusbar}},
78 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .is_string = 1, .func = update_title}},
79 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .is_string = 1, .func = update_title}},
80 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .is_string = 1, .func = update_title}},
81 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .is_string = 1, .func = update_title}},
82 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .is_string = 0, .func = NULL}},
83 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .is_string = 0, .func = NULL}},
84 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .is_string = 0, .func = NULL}},
85 { "modkey" , {.ptr = (void *)&uzbl.behave.modkey, .is_string = 0, .func = NULL}},
86 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .is_string = 1, .func = update_title}},
87 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .is_string = 1, .func = update_title}},
88 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .is_string = 1, .func = update_title}},
89 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .is_string = 1, .func = update_title}},
90 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .is_string = 1, .func = update_title}},
91 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .is_string = 1, .func = update_title}},
92 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .is_string = 1, .func = NULL}},
93 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .is_string = 1, .func = NULL}},
94 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .is_string = 0, .func = NULL}},
95 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .is_string = 0, .func = NULL}},
96 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .is_string = 0, .func = NULL}},
97 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .is_string = 1, .func = NULL}},
98 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .is_string = 1, .func = set_proxy_url}},
99 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .is_string = 1, .func = NULL}},
100 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .is_string = 0, .func = NULL}},
101 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .is_string = 1, .func = NULL}},
102 { NULL, {.ptr = NULL, .is_string = 0, .func = NULL}}
103 }, *n2v_p = var_name_to_ptr;
109 { "SHIFT", GDK_SHIFT_MASK }, // shift
110 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
111 { "CONTROL", GDK_CONTROL_MASK }, // control
112 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
113 { "MOD2", GDK_MOD2_MASK }, // 5th mod
114 { "MOD3", GDK_MOD3_MASK }, // 6th mod
115 { "MOD4", GDK_MOD4_MASK }, // 7th mod
116 { "MOD5", GDK_MOD5_MASK }, // 8th mod
117 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
118 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
119 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
120 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
121 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
122 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
123 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
124 { "META", GDK_META_MASK }, // meta (since 2.10)
129 dump_vars(gpointer key, gpointer val, gpointer data) {
135 is_string = c->is_string;
138 printf("KEY: %s\tVAL: %s\tIS_STRING: %s\n",
139 (char *)key, (char *)*c->ptr, "YES");
141 printf("KEY: %s\tVAL: %d\tIS_STRING: %s\n",
142 (char *)key, (int)*c->ptr, "NO");
146 /* construct a hash from the var_name_to_ptr array for quick access */
148 make_var_to_name_hash() {
149 uzbl.comm.proto_var = g_hash_table_new(g_str_hash, g_str_equal);
151 g_hash_table_insert(uzbl.comm.proto_var, n2v_p->name, (gpointer) &n2v_p->cp);
156 /* commandline arguments (set initial values for the state variables) */
157 static GOptionEntry entries[] =
159 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
160 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
161 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
162 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
163 { NULL, 0, 0, 0, NULL, NULL, NULL }
166 typedef void (*Command)(WebKitWebView*, const char *);
168 /* --- UTILITY FUNCTIONS --- */
174 snprintf(tmp, sizeof(tmp), "%i", val);
175 return g_strdup(tmp);
179 str_replace (const char* search, const char* replace, const char* string) {
183 buf = g_strsplit (string, search, -1);
184 ret = g_strjoinv (replace, buf);
191 setup_signal(int signr, sigfunc *shandler) {
192 struct sigaction nh, oh;
194 nh.sa_handler = shandler;
195 sigemptyset(&nh.sa_mask);
198 if(sigaction(signr, &nh, &oh) < 0)
206 if (uzbl.behave.fifo_dir)
207 unlink (uzbl.comm.fifo_path);
208 if (uzbl.behave.socket_dir)
209 unlink (uzbl.comm.socket_path);
211 g_free(uzbl.state.executable_path);
212 g_string_free(uzbl.state.keycmd, TRUE);
213 g_hash_table_destroy(uzbl.bindings);
214 g_hash_table_destroy(uzbl.behave.commands);
218 /* --- SIGNAL HANDLER --- */
221 catch_sigterm(int s) {
227 catch_sigint(int s) {
233 /* --- CALLBACKS --- */
236 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
239 (void) navigation_action;
240 (void) policy_decision;
242 const gchar* uri = webkit_network_request_get_uri (request);
243 if (uzbl.state.verbose)
244 printf("New window requested -> %s \n", uri);
245 new_window_load_uri(uri);
250 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
254 if (uzbl.state.selected_url != NULL) {
255 if (uzbl.state.verbose)
256 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
257 new_window_load_uri(uzbl.state.selected_url);
259 if (uzbl.state.verbose)
260 printf("New web view -> %s\n","Nothing to open, exiting");
266 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
269 if (uzbl.behave.download_handler) {
270 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
271 if (uzbl.state.verbose)
272 printf("Download -> %s\n",uri);
273 /* if urls not escaped, we may have to escape and quote uri before this call */
274 run_handler(uzbl.behave.download_handler, uri);
279 /* scroll a bar in a given direction */
281 scroll (GtkAdjustment* bar, const char *param) {
285 amount = g_ascii_strtod(param, &end);
286 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
287 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
290 static void scroll_begin(WebKitWebView* page, const char *param) {
291 (void) page; (void) param;
292 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
295 static void scroll_end(WebKitWebView* page, const char *param) {
296 (void) page; (void) param;
297 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
298 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
301 static void scroll_vert(WebKitWebView* page, const char *param) {
303 scroll(uzbl.gui.bar_v, param);
306 static void scroll_horz(WebKitWebView* page, const char *param) {
308 scroll(uzbl.gui.bar_h, param);
313 if (!uzbl.behave.show_status) {
314 gtk_widget_hide(uzbl.gui.mainbar);
316 gtk_widget_show(uzbl.gui.mainbar);
322 toggle_status_cb (WebKitWebView* page, const char *param) {
326 if (uzbl.behave.show_status) {
327 gtk_widget_hide(uzbl.gui.mainbar);
329 gtk_widget_show(uzbl.gui.mainbar);
331 uzbl.behave.show_status = !uzbl.behave.show_status;
336 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
340 //Set selected_url state variable
341 g_free(uzbl.state.selected_url);
342 uzbl.state.selected_url = NULL;
344 uzbl.state.selected_url = g_strdup(link);
350 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
354 if (uzbl.gui.main_title)
355 g_free (uzbl.gui.main_title);
356 uzbl.gui.main_title = g_strdup (title);
361 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
364 uzbl.gui.sbar.load_progress = progress;
369 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
373 if (uzbl.behave.load_finish_handler)
374 run_handler(uzbl.behave.load_finish_handler, "");
378 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
382 if (uzbl.behave.load_start_handler)
383 run_handler(uzbl.behave.load_start_handler, "");
387 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
390 free (uzbl.state.uri);
391 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
392 uzbl.state.uri = g_string_free (newuri, FALSE);
393 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
394 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
397 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
398 if (uzbl.behave.load_commit_handler)
399 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
403 destroy_cb (GtkWidget* widget, gpointer data) {
411 if (uzbl.behave.history_handler) {
413 struct tm * timeinfo;
416 timeinfo = localtime ( &rawtime );
417 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
418 run_handler(uzbl.behave.history_handler, date);
423 /* VIEW funcs (little webkit wrappers) */
424 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
426 VIEWFUNC(reload_bypass_cache)
427 VIEWFUNC(stop_loading)
434 /* -- command to callback/function map for things we cannot attach to any signals */
437 static struct {char *name; Command command;} cmdlist[] =
439 { "back", view_go_back },
440 { "forward", view_go_forward },
441 { "scroll_vert", scroll_vert },
442 { "scroll_horz", scroll_horz },
443 { "scroll_begin", scroll_begin },
444 { "scroll_end", scroll_end },
445 { "reload", view_reload, },
446 { "reload_ign_cache", view_reload_bypass_cache},
447 { "stop", view_stop_loading, },
448 { "zoom_in", view_zoom_in, }, //Can crash (when max zoom reached?).
449 { "zoom_out", view_zoom_out, },
451 { "script", run_js },
452 { "toggle_status", toggle_status_cb },
455 { "exit", close_uzbl },
456 { "search", search_forward_text },
457 { "search_reverse", search_reverse_text },
458 { "insert_mode", set_insert_mode },
466 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
468 for (i = 0; i < LENGTH(cmdlist); i++)
469 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
472 /* -- CORE FUNCTIONS -- */
475 free_action(gpointer act) {
476 Action *action = (Action*)act;
477 g_free(action->name);
479 g_free(action->param);
484 new_action(const gchar *name, const gchar *param) {
485 Action *action = g_new(Action, 1);
487 action->name = g_strdup(name);
489 action->param = g_strdup(param);
491 action->param = NULL;
497 file_exists (const char * filename) {
498 return (access(filename, F_OK) == 0);
502 set_insert_mode(WebKitWebView *page, const gchar *param) {
506 uzbl.behave.insert_mode = TRUE;
511 load_uri (WebKitWebView * web_view, const gchar *param) {
513 GString* newuri = g_string_new (param);
514 if (g_strrstr (param, "://") == NULL)
515 g_string_prepend (newuri, "http://");
516 /* if we do handle cookies, ask our handler for them */
517 webkit_web_view_load_uri (web_view, newuri->str);
518 g_string_free (newuri, TRUE);
523 run_js (WebKitWebView * web_view, const gchar *param) {
525 webkit_web_view_execute_script (web_view, param);
529 search_text (WebKitWebView *page, const char *param, const gboolean forward) {
530 if ((param) && (param[0] != '\0')) {
531 uzbl.state.searchtx = g_strdup(param);
533 if (uzbl.state.searchtx != NULL) {
534 if (uzbl.state.verbose)
535 printf ("Searching: %s\n", uzbl.state.searchtx);
536 webkit_web_view_unmark_text_matches (page);
537 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
538 webkit_web_view_set_highlight_text_matches (page, TRUE);
539 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
540 g_free(uzbl.state.searchtx);
541 uzbl.state.searchtx = NULL;
546 search_forward_text (WebKitWebView *page, const char *param) {
547 search_text(page, param, TRUE);
551 search_reverse_text (WebKitWebView *page, const char *param) {
552 search_text(page, param, FALSE);
556 new_window_load_uri (const gchar * uri) {
557 GString* to_execute = g_string_new ("");
558 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
560 for (i = 0; entries[i].long_name != NULL; i++) {
561 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
562 gchar** str = (gchar**)entries[i].arg_data;
564 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
568 if (uzbl.state.verbose)
569 printf("\n%s\n", to_execute->str);
570 g_spawn_command_line_async (to_execute->str, NULL);
571 g_string_free (to_execute, TRUE);
575 close_uzbl (WebKitWebView *page, const char *param) {
581 /* --Statusbar functions-- */
583 build_progressbar_ascii(int percent) {
587 GString *bar = g_string_new("");
589 l = (double)percent*((double)width/100.);
590 l = (int)(l+.5)>=(int)l ? l+.5 : l;
592 for(i=0; i<(int)l; i++)
593 g_string_append(bar, "=");
596 g_string_append(bar, "·");
598 return g_string_free(bar, FALSE);
603 const GScannerConfig scan_config = {
606 ) /* cset_skip_characters */,
611 ) /* cset_identifier_first */,
618 ) /* cset_identifier_nth */,
619 ( "" ) /* cpair_comment_single */,
621 TRUE /* case_sensitive */,
623 FALSE /* skip_comment_multi */,
624 FALSE /* skip_comment_single */,
625 FALSE /* scan_comment_multi */,
626 TRUE /* scan_identifier */,
627 TRUE /* scan_identifier_1char */,
628 FALSE /* scan_identifier_NULL */,
629 TRUE /* scan_symbols */,
630 FALSE /* scan_binary */,
631 FALSE /* scan_octal */,
632 FALSE /* scan_float */,
633 FALSE /* scan_hex */,
634 FALSE /* scan_hex_dollar */,
635 FALSE /* scan_string_sq */,
636 FALSE /* scan_string_dq */,
637 TRUE /* numbers_2_int */,
638 FALSE /* int_2_float */,
639 FALSE /* identifier_2_string */,
640 FALSE /* char_2_token */,
641 FALSE /* symbol_2_token */,
642 TRUE /* scope_0_fallback */,
647 uzbl.scan = g_scanner_new(&scan_config);
648 while(symp->symbol_name) {
649 g_scanner_scope_add_symbol(uzbl.scan, 0,
651 GINT_TO_POINTER(symp->symbol_token));
657 expand_template(const char *template) {
658 if(!template) return NULL;
660 GTokenType token = G_TOKEN_NONE;
661 GString *ret = g_string_new("");
665 g_scanner_input_text(uzbl.scan, template, strlen(template));
666 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
667 token = g_scanner_get_next_token(uzbl.scan);
669 if(token == G_TOKEN_SYMBOL) {
670 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
673 buf = uzbl.state.uri?
674 g_markup_printf_escaped("%s", uzbl.state.uri) :
676 g_string_append(ret, buf);
680 buf = itos(uzbl.gui.sbar.load_progress);
681 g_string_append(ret, buf);
684 case SYM_LOADPRGSBAR:
685 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
686 g_string_append(ret, buf);
690 buf = uzbl.gui.main_title?
691 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
693 g_string_append(ret, buf);
696 case SYM_SELECTED_URI:
697 buf = uzbl.state.selected_url?
698 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
700 g_string_append(ret, buf);
704 buf = itos(uzbl.xwin);
706 uzbl.state.instance_name?uzbl.state.instance_name:buf);
710 buf = uzbl.state.keycmd->str?
711 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
713 g_string_append(ret, buf);
718 uzbl.behave.insert_mode?"[I]":"[C]");
722 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
726 buf = itos(WEBKIT_MAJOR_VERSION);
727 g_string_append(ret, buf);
731 buf = itos(WEBKIT_MINOR_VERSION);
732 g_string_append(ret, buf);
736 buf = itos(WEBKIT_MICRO_VERSION);
737 g_string_append(ret, buf);
741 g_string_append(ret, uzbl.state.unameinfo.sysname);
744 g_string_append(ret, uzbl.state.unameinfo.nodename);
747 g_string_append(ret, uzbl.state.unameinfo.release);
750 g_string_append(ret, uzbl.state.unameinfo.version);
753 g_string_append(ret, uzbl.state.unameinfo.machine);
756 g_string_append(ret, ARCH);
760 g_string_append(ret, uzbl.state.unameinfo.domainname);
764 g_string_append(ret, COMMIT);
770 else if(token == G_TOKEN_INT) {
771 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
772 g_string_append(ret, buf);
775 else if(token == G_TOKEN_IDENTIFIER) {
776 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
778 else if(token == G_TOKEN_CHAR) {
779 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
783 return g_string_free(ret, FALSE);
785 /* --End Statusbar functions-- */
788 sharg_append(GArray *a, const gchar *str) {
789 const gchar *s = (str ? str : "");
790 g_array_append_val(a, s);
793 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
795 run_command (const gchar *command, const guint npre, const gchar **args,
796 const gboolean sync, char **stdout) {
797 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
800 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
801 gchar *pid = itos(getpid());
802 gchar *xwin = itos(uzbl.xwin);
804 sharg_append(a, command);
805 for (i = 0; i < npre; i++) /* add n args before the default vars */
806 sharg_append(a, args[i]);
807 sharg_append(a, uzbl.state.config_file);
808 sharg_append(a, pid);
809 sharg_append(a, xwin);
810 sharg_append(a, uzbl.comm.fifo_path);
811 sharg_append(a, uzbl.comm.socket_path);
812 sharg_append(a, uzbl.state.uri);
813 sharg_append(a, uzbl.gui.main_title);
815 for (i = npre; i < g_strv_length((gchar**)args); i++)
816 sharg_append(a, args[i]);
818 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
819 NULL, NULL, stdout, NULL, NULL, &err);
820 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
821 NULL, NULL, NULL, &err);
823 if (uzbl.state.verbose) {
824 GString *s = g_string_new("spawned:");
825 for (i = 0; i < (a->len); i++) {
826 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
827 g_string_append_printf(s, " %s", qarg);
830 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
831 printf("%s\n", s->str);
832 g_string_free(s, TRUE);
835 g_printerr("error on run_command: %s\n", err->message);
840 g_array_free (a, TRUE);
845 split_quoted(const gchar* src, const gboolean unquote) {
846 /* split on unquoted space, return array of strings;
847 remove a layer of quotes and backslashes if unquote */
850 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
851 GString *s = g_string_new ("");
855 for (p = src; *p != '\0'; p++) {
856 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
857 else if (*p == '\\') { g_string_append_c(s, *p++);
858 g_string_append_c(s, *p); }
859 else if ((*p == '"') && unquote && !sq) dq = !dq;
860 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
862 else if ((*p == '\'') && unquote && !dq) sq = !sq;
863 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
865 else if ((*p == ' ') && !dq && !sq) {
866 dup = g_strdup(s->str);
867 g_array_append_val(a, dup);
868 g_string_truncate(s, 0);
869 } else g_string_append_c(s, *p);
871 dup = g_strdup(s->str);
872 g_array_append_val(a, dup);
873 ret = (gchar**)a->data;
874 g_array_free (a, FALSE);
875 g_string_free (s, FALSE);
880 spawn(WebKitWebView *web_view, const char *param) {
882 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
883 gchar **cmd = split_quoted(param, TRUE);
884 if (cmd) run_command(cmd[0], 0, &cmd[1], FALSE, NULL);
885 g_strfreev ((gchar**)cmd);
889 spawn_sh(WebKitWebView *web_view, const char *param) {
891 if (!uzbl.behave.shell_cmd) {
892 g_printerr ("spawn_sh: shell_cmd is not set!\n");
897 gchar *spacer = g_strdup("");
898 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
899 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
900 gchar **p = split_quoted(param, TRUE);
901 for (i = 1; i < g_strv_length(cmd); i++)
902 sharg_append(a, cmd[i]);
903 sharg_append(a, p[0]); /* the first param comes right after shell_cmd;
904 the rest come after default args */
905 sharg_append(a, spacer);
906 for (i = 1; i < g_strv_length(p); i++)
907 sharg_append(a, p[i]);
908 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, a->data, FALSE, NULL);
912 g_array_free (a, FALSE);
916 parse_command(const char *cmd, const char *param) {
919 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd)))
920 c(uzbl.gui.web_view, param);
922 fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
928 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
929 G_REGEX_OPTIMIZE, 0, NULL);
930 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
931 G_REGEX_OPTIMIZE, 0, NULL);
932 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
933 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
934 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
935 G_REGEX_OPTIMIZE, 0, NULL);
936 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
937 G_REGEX_OPTIMIZE, 0, NULL);
941 get_var_value(gchar *name) {
944 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
946 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
948 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
957 if(*uzbl.net.proxy_url == ' '
958 || uzbl.net.proxy_url == NULL) {
959 soup_session_remove_feature_by_type(uzbl.net.soup_session,
960 (GType) SOUP_SESSION_PROXY_URI);
963 suri = soup_uri_new(uzbl.net.proxy_url);
964 g_object_set(G_OBJECT(uzbl.net.soup_session),
965 SOUP_SESSION_PROXY_URI,
974 load_uri(uzbl.gui.web_view, uzbl.state.uri);
979 gtk_widget_ref(uzbl.gui.scrolled_win);
980 gtk_widget_ref(uzbl.gui.mainbar);
981 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
982 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
984 if(uzbl.behave.status_top) {
985 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
986 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
989 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
990 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
992 gtk_widget_unref(uzbl.gui.scrolled_win);
993 gtk_widget_unref(uzbl.gui.mainbar);
994 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
999 var_is(const char *x, const char *y) {
1000 return (strcmp(x, y) == 0 ? TRUE : FALSE );
1004 set_var_value(gchar *name, gchar *val) {
1006 uzbl_cmdprop *c = NULL;
1010 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1011 /* check for the variable type */
1014 *c->ptr = g_strdup(val);
1016 *c->ptr = (int)strtoul(val, &endp, 10);
1019 /* invoke a command specific function */
1023 /* this will be removed as soon as we have converted to
1024 * the callback interface
1028 if(var_is("fifo_dir", name)) {
1030 buf = init_fifo(val);
1031 p = buf?buf:g_strdup("");
1033 else if(var_is("socket_dir", name)) {
1035 buf = init_socket(val);
1036 p = buf?buf:g_strdup("");
1038 else if(var_is("modkey", name)) {
1041 p = g_utf8_strup(val, -1);
1042 uzbl.behave.modmask = 0;
1043 for (i = 0; modkeys[i].key != NULL; i++) {
1044 if (g_strrstr(p, modkeys[i].key))
1045 uzbl.behave.modmask |= modkeys[i].mask;
1048 else if(var_is("useragent", name)) {
1050 buf = set_useragent(val);
1051 p = buf?buf:g_strdup("");
1053 else if(var_is("always_insert_mode", name)) {
1054 uzbl.behave.insert_mode =
1055 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1058 else if (var_is("max_conns", name)) {
1059 g_object_set(G_OBJECT(uzbl.net.soup_session),
1060 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1062 else if (var_is("max_conns_host", name)) {
1063 g_object_set(G_OBJECT(uzbl.net.soup_session),
1064 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1066 else if (var_is("http_debug", name)) {
1067 soup_session_remove_feature
1068 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1069 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1070 /*g_free(uzbl.net.soup_logger);*/
1072 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1073 soup_session_add_feature(uzbl.net.soup_session,
1074 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1076 else if (var_is("default_font_size", name)) {
1077 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1078 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1080 else if (var_is("minimum_font_size", name)) {
1081 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1082 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1089 runcmd(WebKitWebView* page, const char *param) {
1091 parse_cmd_line(param);
1095 parse_cmd_line(const char *ctl_line) {
1099 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1100 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1101 if(tokens[0][0] == 0) {
1102 set_var_value(tokens[1], tokens[2]);
1106 printf("Error in command: %s\n", tokens[0]);
1109 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1110 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1111 if(tokens[0][0] == 0) {
1112 get_var_value(tokens[1]);
1116 printf("Error in command: %s\n", tokens[0]);
1119 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1120 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1121 if(tokens[0][0] == 0) {
1122 add_binding(tokens[1], tokens[2]);
1126 printf("Error in command: %s\n", tokens[0]);
1129 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1130 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1131 if(tokens[0][0] == 0) {
1132 parse_command(tokens[1], tokens[2]);
1136 printf("Error in command: %s\n", tokens[0]);
1138 /* KEYCMD command */
1139 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1140 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1141 if(tokens[0][0] == 0) {
1142 /* should incremental commands want each individual "keystroke"
1143 sent in a loop or the whole string in one go like now? */
1144 g_string_assign(uzbl.state.keycmd, tokens[1]);
1146 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1153 else if( (ctl_line[0] == '#')
1154 || (ctl_line[0] == ' ')
1155 || (ctl_line[0] == '\n'))
1156 ; /* ignore these lines */
1158 printf("Command not understood (%s)\n", ctl_line);
1164 build_stream_name(int type, const gchar* dir) {
1166 State *s = &uzbl.state;
1169 xwin_str = itos((int)uzbl.xwin);
1171 str = g_strdup_printf
1172 ("%s/uzbl_fifo_%s", dir,
1173 s->instance_name ? s->instance_name : xwin_str);
1174 } else if (type == SOCKET) {
1175 str = g_strdup_printf
1176 ("%s/uzbl_socket_%s", dir,
1177 s->instance_name ? s->instance_name : xwin_str );
1184 control_fifo(GIOChannel *gio, GIOCondition condition) {
1185 if (uzbl.state.verbose)
1186 printf("triggered\n");
1191 if (condition & G_IO_HUP)
1192 g_error ("Fifo: Read end of pipe died!\n");
1195 g_error ("Fifo: GIOChannel broke\n");
1197 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1198 if (ret == G_IO_STATUS_ERROR) {
1199 g_error ("Fifo: Error reading: %s\n", err->message);
1203 parse_cmd_line(ctl_line);
1210 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1211 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1212 if (unlink(uzbl.comm.fifo_path) == -1)
1213 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1214 g_free(uzbl.comm.fifo_path);
1215 uzbl.comm.fifo_path = NULL;
1218 if (*dir == ' ') { /* space unsets the variable */
1222 GIOChannel *chan = NULL;
1223 GError *error = NULL;
1224 gchar *path = build_stream_name(FIFO, dir);
1226 if (!file_exists(path)) {
1227 if (mkfifo (path, 0666) == 0) {
1228 // 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.
1229 chan = g_io_channel_new_file(path, "r+", &error);
1231 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1232 if (uzbl.state.verbose)
1233 printf ("init_fifo: created successfully as %s\n", path);
1234 uzbl.comm.fifo_path = path;
1236 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1237 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1238 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1239 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1241 /* if we got this far, there was an error; cleanup */
1242 if (error) g_error_free (error);
1248 control_stdin(GIOChannel *gio, GIOCondition condition) {
1250 gchar *ctl_line = NULL;
1253 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1254 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1257 parse_cmd_line(ctl_line);
1265 GIOChannel *chan = NULL;
1266 GError *error = NULL;
1268 chan = g_io_channel_unix_new(fileno(stdin));
1270 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1271 g_error ("Stdin: could not add watch\n");
1273 if (uzbl.state.verbose)
1274 printf ("Stdin: watch added successfully\n");
1277 g_error ("Stdin: Error while opening: %s\n", error->message);
1279 if (error) g_error_free (error);
1283 control_socket(GIOChannel *chan) {
1284 struct sockaddr_un remote;
1285 char buffer[512], *ctl_line;
1287 int sock, clientsock, n, done;
1290 sock = g_io_channel_unix_get_fd(chan);
1292 memset (buffer, 0, sizeof (buffer));
1294 t = sizeof (remote);
1295 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1299 memset (temp, 0, sizeof (temp));
1300 n = recv (clientsock, temp, 128, 0);
1302 buffer[strlen (buffer)] = '\0';
1306 strcat (buffer, temp);
1309 if (strcmp (buffer, "\n") < 0) {
1310 buffer[strlen (buffer) - 1] = '\0';
1312 buffer[strlen (buffer)] = '\0';
1315 ctl_line = g_strdup(buffer);
1316 parse_cmd_line (ctl_line);
1319 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1320 GError *error = NULL;
1323 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1324 if (ret == G_IO_STATUS_ERROR)
1325 g_error ("Error reading: %s\n", error->message);
1327 printf("Got line %s (%u bytes) \n",ctl_line, len);
1329 parse_line(ctl_line);
1337 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1338 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1339 if (unlink(uzbl.comm.socket_path) == -1)
1340 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1341 g_free(uzbl.comm.socket_path);
1342 uzbl.comm.socket_path = NULL;
1350 GIOChannel *chan = NULL;
1352 struct sockaddr_un local;
1353 gchar *path = build_stream_name(SOCKET, dir);
1355 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1357 local.sun_family = AF_UNIX;
1358 strcpy (local.sun_path, path);
1359 unlink (local.sun_path);
1361 len = strlen (local.sun_path) + sizeof (local.sun_family);
1362 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1363 if (uzbl.state.verbose)
1364 printf ("init_socket: opened in %s\n", path);
1367 if( (chan = g_io_channel_unix_new(sock)) ) {
1368 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1369 uzbl.comm.socket_path = path;
1372 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1374 /* if we got this far, there was an error; cleanup */
1381 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1382 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1384 // this function may be called very early when the templates are not set (yet), hence the checks
1386 update_title (void) {
1387 Behaviour *b = &uzbl.behave;
1390 if (b->show_status) {
1391 if (b->title_format_short) {
1392 parsed = expand_template(b->title_format_short);
1393 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1396 if (b->status_format) {
1397 parsed = expand_template(b->status_format);
1398 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1401 if (b->status_background) {
1403 gdk_color_parse (b->status_background, &color);
1404 //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)
1405 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1408 if (b->title_format_long) {
1409 parsed = expand_template(b->title_format_long);
1410 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1417 key_press_cb (WebKitWebView* page, GdkEventKey* event)
1419 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1423 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1424 || 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)
1427 /* turn off insert mode (if always_insert_mode is not used) */
1428 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1429 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1434 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1437 if (event->keyval == GDK_Escape) {
1438 g_string_truncate(uzbl.state.keycmd, 0);
1443 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1444 if (event->keyval == GDK_Insert) {
1446 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1447 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1449 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1452 g_string_append (uzbl.state.keycmd, str);
1459 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1460 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1464 gboolean key_ret = FALSE;
1465 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1467 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1469 run_keycmd(key_ret);
1471 if (key_ret) return (!uzbl.behave.insert_mode);
1476 run_keycmd(const gboolean key_ret) {
1477 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1479 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1480 g_string_truncate(uzbl.state.keycmd, 0);
1481 parse_command(action->name, action->param);
1485 /* try if it's an incremental keycmd or one that takes args, and run it */
1486 GString* short_keys = g_string_new ("");
1487 GString* short_keys_inc = g_string_new ("");
1489 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1490 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1491 g_string_assign(short_keys_inc, short_keys->str);
1492 g_string_append_c(short_keys, '_');
1493 g_string_append_c(short_keys_inc, '*');
1495 gboolean exec_now = FALSE;
1496 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1497 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1498 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1499 if (key_ret) { /* just quit the incremental command on return */
1500 g_string_truncate(uzbl.state.keycmd, 0);
1502 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1506 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1507 GString* actionname = g_string_new ("");
1508 GString* actionparam = g_string_new ("");
1509 g_string_erase (parampart, 0, i+1);
1511 g_string_printf (actionname, action->name, parampart->str);
1513 g_string_printf (actionparam, action->param, parampart->str);
1514 parse_command(actionname->str, actionparam->str);
1515 g_string_free (actionname, TRUE);
1516 g_string_free (actionparam, TRUE);
1517 g_string_free (parampart, TRUE);
1519 g_string_truncate(uzbl.state.keycmd, 0);
1523 g_string_truncate(short_keys, short_keys->len - 1);
1525 g_string_free (short_keys, TRUE);
1526 g_string_free (short_keys_inc, TRUE);
1533 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1534 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
1536 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1537 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1539 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1540 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1541 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1542 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1543 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1544 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1545 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1546 g_signal_connect (G_OBJECT (g->web_view), "key-press-event", G_CALLBACK (key_press_cb), g->web_view);
1547 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1548 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1549 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1551 return scrolled_window;
1558 g->mainbar = gtk_hbox_new (FALSE, 0);
1560 g->mainbar_label = gtk_label_new ("");
1561 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1562 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1563 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1564 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1565 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1570 GtkWidget* create_window () {
1571 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1572 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1573 gtk_widget_set_name (window, "Uzbl browser");
1574 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1580 run_handler (const gchar *act, const gchar *args) {
1581 char **parts = g_strsplit(act, " ", 2);
1583 else if ((g_strcmp0(parts[0], "spawn") == 0)
1584 || (g_strcmp0(parts[0], "sh") == 0)) {
1586 GString *a = g_string_new ("");
1588 spawnparts = split_quoted(parts[1], FALSE);
1589 g_string_append_printf(a, "%s", spawnparts[0]);
1590 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1591 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1592 g_string_append_printf(a, " %s", spawnparts[i]);
1593 parse_command(parts[0], a->str);
1594 g_string_free (a, TRUE);
1595 g_strfreev (spawnparts);
1597 parse_command(parts[0], parts[1]);
1602 add_binding (const gchar *key, const gchar *act) {
1603 char **parts = g_strsplit(act, " ", 2);
1610 if (uzbl.state.verbose)
1611 printf ("Binding %-10s : %s\n", key, act);
1613 action = new_action(parts[0], parts[1]);
1614 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1620 get_xdg_var (XDG_Var xdg) {
1621 const gchar* actual_value = getenv (xdg.environmental);
1622 const gchar* home = getenv ("HOME");
1624 gchar* return_value = str_replace ("~", home, actual_value);
1626 if (! actual_value || strcmp (actual_value, "") == 0) {
1627 if (xdg.default_value) {
1628 return_value = str_replace ("~", home, xdg.default_value);
1630 return_value = NULL;
1633 return return_value;
1637 find_xdg_file (int xdg_type, char* filename) {
1638 /* xdg_type = 0 => config
1639 xdg_type = 1 => data
1640 xdg_type = 2 => cache*/
1642 gchar* temporary_file = malloc (1024);
1643 gchar* temporary_string = NULL;
1647 buf = get_xdg_var (XDG[xdg_type]);
1648 strcpy (temporary_file, buf);
1649 strcat (temporary_file, filename);
1652 if (! file_exists (temporary_file) && xdg_type != 2) {
1653 buf = get_xdg_var (XDG[3 + xdg_type]);
1654 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1657 while (temporary_string && ! file_exists (temporary_file)) {
1658 strcpy (temporary_file, temporary_string);
1659 strcat (temporary_file, filename);
1660 temporary_string = (char * ) strtok_r (NULL, ":", &saveptr);
1664 if (file_exists (temporary_file)) {
1665 return temporary_file;
1673 State *s = &uzbl.state;
1674 Network *n = &uzbl.net;
1676 uzbl.behave.reset_command_mode = 1;
1678 if (!s->config_file) {
1679 s->config_file = find_xdg_file (0, "/uzbl/config");
1682 if (s->config_file) {
1683 GIOChannel *chan = NULL;
1684 gchar *readbuf = NULL;
1687 chan = g_io_channel_new_file(s->config_file, "r", NULL);
1690 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL)
1691 == G_IO_STATUS_NORMAL) {
1692 parse_cmd_line(readbuf);
1696 g_io_channel_unref (chan);
1697 if (uzbl.state.verbose)
1698 printf ("Config %s loaded\n", s->config_file);
1700 fprintf(stderr, "uzbl: error loading file%s\n", s->config_file);
1703 if (uzbl.state.verbose)
1704 printf ("No configuration file loaded.\n");
1706 if (!uzbl.behave.status_format)
1707 set_var_value("status_format", STATUS_DEFAULT);
1708 if (!uzbl.behave.title_format_long)
1709 set_var_value("title_format_long", TITLE_LONG_DEFAULT);
1710 if (!uzbl.behave.title_format_short)
1711 set_var_value("title_format_short", TITLE_SHORT_DEFAULT);
1714 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1718 set_useragent(gchar *val) {
1723 gchar *ua = expand_template(val);
1725 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1729 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1732 if (!uzbl.behave.cookie_handler) return;
1734 gchar * stdout = NULL;
1735 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1736 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1737 gchar *action = g_strdup ("GET");
1738 SoupURI * soup_uri = soup_message_get_uri(msg);
1739 sharg_append(a, action);
1740 sharg_append(a, soup_uri->host);
1741 sharg_append(a, soup_uri->path);
1742 run_command(uzbl.behave.cookie_handler, 0, a->data, TRUE, &stdout); /* TODO: use handler */
1743 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1745 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1748 g_array_free(a, TRUE);
1752 save_cookies (SoupMessage *msg, gpointer user_data){
1756 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1757 cookie = soup_cookie_to_set_cookie_header(ck->data);
1758 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1759 SoupURI * soup_uri = soup_message_get_uri(msg);
1760 gchar *action = strdup("PUT");
1761 sharg_append(a, action);
1762 sharg_append(a, soup_uri->host);
1763 sharg_append(a, soup_uri->path);
1764 sharg_append(a, cookie);
1765 run_command(uzbl.behave.cookie_handler, 0, a->data, FALSE, NULL);
1768 g_array_free(a, TRUE);
1775 main (int argc, char* argv[]) {
1776 gtk_init (&argc, &argv);
1777 if (!g_thread_supported ())
1778 g_thread_init (NULL);
1780 uzbl.state.executable_path = g_strdup(argv[0]);
1781 uzbl.state.selected_url = NULL;
1782 uzbl.state.searchtx = NULL;
1784 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1785 g_option_context_add_main_entries (context, entries, NULL);
1786 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1787 g_option_context_parse (context, &argc, &argv, NULL);
1788 g_option_context_free(context);
1789 /* initialize hash table */
1790 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1792 uzbl.net.soup_session = webkit_get_default_session();
1793 uzbl.state.keycmd = g_string_new("");
1795 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1796 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1797 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1798 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1800 if(uname(&uzbl.state.unameinfo) == -1)
1801 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1806 make_var_to_name_hash();
1809 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1811 uzbl.gui.scrolled_win = create_browser();
1814 /* initial packing */
1815 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1816 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1818 uzbl.gui.main_window = create_window ();
1819 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1822 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1823 gtk_widget_show_all (uzbl.gui.main_window);
1824 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1826 if (uzbl.state.verbose) {
1827 printf("Uzbl start location: %s\n", argv[0]);
1828 printf("window_id %i\n",(int) uzbl.xwin);
1829 printf("pid %i\n", getpid ());
1830 printf("name: %s\n", uzbl.state.instance_name);
1833 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1834 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1835 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1836 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1837 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1841 if (!uzbl.behave.show_status)
1842 gtk_widget_hide(uzbl.gui.mainbar);
1849 load_uri (uzbl.gui.web_view, uzbl.state.uri);
1854 return EXIT_SUCCESS;
1857 /* vi: set et ts=4: */