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>
60 typedef void (*Command)(WebKitWebView*, GArray *argv);
62 /* commandline arguments (set initial values for the state variables) */
63 static GOptionEntry entries[] =
65 { "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
66 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose, "Whether to print all messages or just errors.", NULL },
67 { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name, "Name of the current instance (defaults to Xorg window id)", "NAME" },
68 { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
69 { NULL, 0, 0, 0, NULL, NULL, NULL }
73 /* associate command names to their properties */
74 typedef const struct {
80 enum {TYPE_INT, TYPE_STRING};
85 } var_name_to_ptr[] = {
86 /* variable name pointer to variable in code variable type callback function */
87 /* ------------------------------------------------------------------------------------------------------------------- */
88 { "uri", {.ptr = (void *)&uzbl.state.uri, .type = TYPE_STRING, .func = cmd_load_uri}},
89 { "status_message", {.ptr = (void *)&uzbl.gui.sbar.msg, .type = TYPE_STRING, .func = update_title}},
90 { "show_status", {.ptr = (void *)&uzbl.behave.show_status, .type = TYPE_INT, .func = cmd_set_status}},
91 { "status_top", {.ptr = (void *)&uzbl.behave.status_top, .type = TYPE_INT, .func = move_statusbar}},
92 { "status_format", {.ptr = (void *)&uzbl.behave.status_format, .type = TYPE_STRING, .func = update_title}},
93 { "status_pbar_done", {.ptr = (void *)&uzbl.gui.sbar.progress_s, .type = TYPE_STRING, .func = update_title}},
94 { "status_pbar_pending",{.ptr = (void *)&uzbl.gui.sbar.progress_u, .type = TYPE_STRING, .func = update_title}},
95 { "status_pbar_width", {.ptr = (void *)&uzbl.gui.sbar.progress_w, .type = TYPE_INT, .func = update_title}},
96 { "status_background", {.ptr = (void *)&uzbl.behave.status_background, .type = TYPE_STRING, .func = update_title}},
97 { "title_format_long", {.ptr = (void *)&uzbl.behave.title_format_long, .type = TYPE_STRING, .func = update_title}},
98 { "title_format_short", {.ptr = (void *)&uzbl.behave.title_format_short, .type = TYPE_STRING, .func = update_title}},
99 { "insert_mode", {.ptr = (void *)&uzbl.behave.insert_mode, .type = TYPE_INT, .func = NULL}},
100 { "always_insert_mode", {.ptr = (void *)&uzbl.behave.always_insert_mode, .type = TYPE_INT, .func = cmd_always_insert_mode}},
101 { "reset_command_mode", {.ptr = (void *)&uzbl.behave.reset_command_mode, .type = TYPE_INT, .func = NULL}},
102 { "modkey", {.ptr = (void *)&uzbl.behave.modkey, .type = TYPE_STRING, .func = cmd_modkey}},
103 { "load_finish_handler",{.ptr = (void *)&uzbl.behave.load_finish_handler, .type = TYPE_STRING, .func = NULL}},
104 { "load_start_handler", {.ptr = (void *)&uzbl.behave.load_start_handler, .type = TYPE_STRING, .func = NULL}},
105 { "load_commit_handler",{.ptr = (void *)&uzbl.behave.load_commit_handler, .type = TYPE_STRING, .func = NULL}},
106 { "history_handler", {.ptr = (void *)&uzbl.behave.history_handler, .type = TYPE_STRING, .func = NULL}},
107 { "download_handler", {.ptr = (void *)&uzbl.behave.download_handler, .type = TYPE_STRING, .func = NULL}},
108 { "cookie_handler", {.ptr = (void *)&uzbl.behave.cookie_handler, .type = TYPE_STRING, .func = NULL}},
109 { "fifo_dir", {.ptr = (void *)&uzbl.behave.fifo_dir, .type = TYPE_STRING, .func = cmd_fifo_dir}},
110 { "socket_dir", {.ptr = (void *)&uzbl.behave.socket_dir, .type = TYPE_STRING, .func = cmd_socket_dir}},
111 { "http_debug", {.ptr = (void *)&uzbl.behave.http_debug, .type = TYPE_INT, .func = cmd_http_debug}},
112 { "default_font_size", {.ptr = (void *)&uzbl.behave.default_font_size, .type = TYPE_INT, .func = cmd_default_font_size}},
113 { "minimum_font_size", {.ptr = (void *)&uzbl.behave.minimum_font_size, .type = TYPE_INT, .func = cmd_minimum_font_size}},
114 { "shell_cmd", {.ptr = (void *)&uzbl.behave.shell_cmd, .type = TYPE_STRING, .func = NULL}},
115 { "proxy_url", {.ptr = (void *)&uzbl.net.proxy_url, .type = TYPE_STRING, .func = set_proxy_url}},
116 { "max_conns", {.ptr = (void *)&uzbl.net.max_conns, .type = TYPE_INT, .func = cmd_max_conns}},
117 { "max_conns_host", {.ptr = (void *)&uzbl.net.max_conns_host, .type = TYPE_INT, .func = cmd_max_conns_host}},
118 { "useragent", {.ptr = (void *)&uzbl.net.useragent, .type = TYPE_STRING, .func = cmd_useragent}},
119 { NULL, {.ptr = NULL, .type = TYPE_INT, .func = NULL}}
120 }, *n2v_p = var_name_to_ptr;
126 { "SHIFT", GDK_SHIFT_MASK }, // shift
127 { "LOCK", GDK_LOCK_MASK }, // capslock or shiftlock, depending on xserver's modmappings
128 { "CONTROL", GDK_CONTROL_MASK }, // control
129 { "MOD1", GDK_MOD1_MASK }, // 4th mod - normally alt but depends on modmappings
130 { "MOD2", GDK_MOD2_MASK }, // 5th mod
131 { "MOD3", GDK_MOD3_MASK }, // 6th mod
132 { "MOD4", GDK_MOD4_MASK }, // 7th mod
133 { "MOD5", GDK_MOD5_MASK }, // 8th mod
134 { "BUTTON1", GDK_BUTTON1_MASK }, // 1st mouse button
135 { "BUTTON2", GDK_BUTTON2_MASK }, // 2nd mouse button
136 { "BUTTON3", GDK_BUTTON3_MASK }, // 3rd mouse button
137 { "BUTTON4", GDK_BUTTON4_MASK }, // 4th mouse button
138 { "BUTTON5", GDK_BUTTON5_MASK }, // 5th mouse button
139 { "SUPER", GDK_SUPER_MASK }, // super (since 2.10)
140 { "HYPER", GDK_HYPER_MASK }, // hyper (since 2.10)
141 { "META", GDK_META_MASK }, // meta (since 2.10)
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);
157 /* --- UTILITY FUNCTIONS --- */
163 snprintf(tmp, sizeof(tmp), "%i", val);
164 return g_strdup(tmp);
168 argv_idx(const GArray *a, const guint idx) { return g_array_index(a, gchar*, idx); }
171 str_replace (const char* search, const char* replace, const char* string) {
175 buf = g_strsplit (string, search, -1);
176 ret = g_strjoinv (replace, buf);
177 //g_strfreev(buf); - segfaults.
183 read_file_by_line (gchar *path) {
184 GIOChannel *chan = NULL;
185 gchar *readbuf = NULL;
187 GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
190 chan = g_io_channel_new_file(path, "r", NULL);
193 while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
194 const gchar* val = g_strdup (readbuf);
195 g_array_append_val (lines, val);
200 g_io_channel_unref (chan);
202 fprintf(stderr, "File '%s' not be read.\n", path);
209 gchar* parseenv (const char* string) {
210 extern char** environ;
211 gchar* newstring = g_strdup (string);
214 while (environ[i] != NULL) {
215 gchar** env = g_strsplit (environ[i], "=", 0);
216 gchar* envname = g_strconcat ("$", env[0], NULL);
218 if (g_strrstr (newstring, envname) != NULL) {
219 newstring = str_replace(envname, env[1], newstring);
223 //g_strfreev (env); //- This still breaks uzbl, but shouldn't. The mystery thickens...
231 setup_signal(int signr, sigfunc *shandler) {
232 struct sigaction nh, oh;
234 nh.sa_handler = shandler;
235 sigemptyset(&nh.sa_mask);
238 if(sigaction(signr, &nh, &oh) < 0)
246 if (uzbl.behave.fifo_dir)
247 unlink (uzbl.comm.fifo_path);
248 if (uzbl.behave.socket_dir)
249 unlink (uzbl.comm.socket_path);
251 g_free(uzbl.state.executable_path);
252 g_string_free(uzbl.state.keycmd, TRUE);
253 g_hash_table_destroy(uzbl.bindings);
254 g_hash_table_destroy(uzbl.behave.commands);
258 /* --- SIGNAL HANDLER --- */
261 catch_sigterm(int s) {
267 catch_sigint(int s) {
273 /* --- CALLBACKS --- */
276 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
279 (void) navigation_action;
280 (void) policy_decision;
282 const gchar* uri = webkit_network_request_get_uri (request);
283 if (uzbl.state.verbose)
284 printf("New window requested -> %s \n", uri);
285 new_window_load_uri(uri);
290 create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data) {
294 if (uzbl.state.selected_url != NULL) {
295 if (uzbl.state.verbose)
296 printf("\nNew web view -> %s\n",uzbl.state.selected_url);
297 new_window_load_uri(uzbl.state.selected_url);
299 if (uzbl.state.verbose)
300 printf("New web view -> %s\n","Nothing to open, exiting");
306 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
309 if (uzbl.behave.download_handler) {
310 const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
311 if (uzbl.state.verbose)
312 printf("Download -> %s\n",uri);
313 /* if urls not escaped, we may have to escape and quote uri before this call */
314 run_handler(uzbl.behave.download_handler, uri);
319 /* scroll a bar in a given direction */
321 scroll (GtkAdjustment* bar, GArray *argv) {
325 amount = g_ascii_strtod(g_array_index(argv, gchar*, 0), &end);
326 if (*end == '%') amount = gtk_adjustment_get_page_size(bar) * amount * 0.01;
327 gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+amount);
330 static void scroll_begin(WebKitWebView* page, GArray *argv) {
331 (void) page; (void) argv;
332 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_lower(uzbl.gui.bar_v));
335 static void scroll_end(WebKitWebView* page, GArray *argv) {
336 (void) page; (void) argv;
337 gtk_adjustment_set_value (uzbl.gui.bar_v, gtk_adjustment_get_upper(uzbl.gui.bar_v) -
338 gtk_adjustment_get_page_size(uzbl.gui.bar_v));
341 static void scroll_vert(WebKitWebView* page, GArray *argv) {
343 scroll(uzbl.gui.bar_v, argv);
346 static void scroll_horz(WebKitWebView* page, GArray *argv) {
348 scroll(uzbl.gui.bar_h, argv);
353 if (!uzbl.behave.show_status) {
354 gtk_widget_hide(uzbl.gui.mainbar);
356 gtk_widget_show(uzbl.gui.mainbar);
362 toggle_status_cb (WebKitWebView* page, GArray *argv) {
366 if (uzbl.behave.show_status) {
367 gtk_widget_hide(uzbl.gui.mainbar);
369 gtk_widget_show(uzbl.gui.mainbar);
371 uzbl.behave.show_status = !uzbl.behave.show_status;
376 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
380 //Set selected_url state variable
381 g_free(uzbl.state.selected_url);
382 uzbl.state.selected_url = NULL;
384 uzbl.state.selected_url = g_strdup(link);
390 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
394 if (uzbl.gui.main_title)
395 g_free (uzbl.gui.main_title);
396 uzbl.gui.main_title = g_strdup (title);
401 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
404 uzbl.gui.sbar.load_progress = progress;
409 load_finish_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
413 if (uzbl.behave.load_finish_handler)
414 run_handler(uzbl.behave.load_finish_handler, "");
418 load_start_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
422 if (uzbl.behave.load_start_handler)
423 run_handler(uzbl.behave.load_start_handler, "");
427 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
430 g_free (uzbl.state.uri);
431 GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
432 uzbl.state.uri = g_string_free (newuri, FALSE);
433 if (uzbl.behave.reset_command_mode && uzbl.behave.insert_mode) {
434 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
437 g_string_truncate(uzbl.state.keycmd, 0); // don't need old commands to remain on new page?
438 if (uzbl.behave.load_commit_handler)
439 run_handler(uzbl.behave.load_commit_handler, uzbl.state.uri);
443 destroy_cb (GtkWidget* widget, gpointer data) {
451 if (uzbl.behave.history_handler) {
453 struct tm * timeinfo;
456 timeinfo = localtime ( &rawtime );
457 strftime (date, 80, "\"%Y-%m-%d %H:%M:%S\"", timeinfo);
458 run_handler(uzbl.behave.history_handler, date);
463 /* VIEW funcs (little webkit wrappers) */
464 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, GArray *argv){(void)argv; webkit_web_view_##name(page);}
466 VIEWFUNC(reload_bypass_cache)
467 VIEWFUNC(stop_loading)
474 /* -- command to callback/function map for things we cannot attach to any signals */
476 static struct {char *name; Command command[2];} cmdlist[] =
477 { /* key function no_split */
478 { "back", {view_go_back, 0} },
479 { "forward", {view_go_forward, 0} },
480 { "scroll_vert", {scroll_vert, 0} },
481 { "scroll_horz", {scroll_horz, 0} },
482 { "scroll_begin", {scroll_begin, 0} },
483 { "scroll_end", {scroll_end, 0} },
484 { "reload", {view_reload, 0}, },
485 { "reload_ign_cache", {view_reload_bypass_cache, 0} },
486 { "stop", {view_stop_loading, 0}, },
487 { "zoom_in", {view_zoom_in, 0}, }, //Can crash (when max zoom reached?).
488 { "zoom_out", {view_zoom_out, 0}, },
489 { "uri", {load_uri, NOSPLIT} },
490 { "js", {run_js, NOSPLIT} },
491 { "script", {run_external_js, 0} },
492 { "toggle_status", {toggle_status_cb, 0} },
493 { "spawn", {spawn, 0} },
494 { "sh", {spawn_sh, 0} },
495 { "exit", {close_uzbl, 0} },
496 { "search", {search_forward_text, NOSPLIT} },
497 { "search_reverse", {search_reverse_text, NOSPLIT} },
498 { "toggle_insert_mode", {toggle_insert_mode, 0} },
499 { "runcmd", {runcmd, NOSPLIT} }
506 uzbl.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
508 for (i = 0; i < LENGTH(cmdlist); i++)
509 g_hash_table_insert(uzbl.behave.commands, cmdlist[i].name, cmdlist[i].command);
512 /* -- CORE FUNCTIONS -- */
515 free_action(gpointer act) {
516 Action *action = (Action*)act;
517 g_free(action->name);
519 g_free(action->param);
524 new_action(const gchar *name, const gchar *param) {
525 Action *action = g_new(Action, 1);
527 action->name = g_strdup(name);
529 action->param = g_strdup(param);
531 action->param = NULL;
537 file_exists (const char * filename) {
538 return (access(filename, F_OK) == 0);
542 toggle_insert_mode(WebKitWebView *page, GArray *argv) {
545 if (argv_idx(argv, 0)) {
546 if (strcmp (argv_idx(argv, 0), "0") == 0) {
547 uzbl.behave.insert_mode = FALSE;
549 uzbl.behave.insert_mode = TRUE;
552 uzbl.behave.insert_mode = ! uzbl.behave.insert_mode;
559 load_uri (WebKitWebView *web_view, GArray *argv) {
560 if (argv_idx(argv, 0)) {
561 GString* newuri = g_string_new (argv_idx(argv, 0));
562 if (g_strrstr (argv_idx(argv, 0), "://") == NULL)
563 g_string_prepend (newuri, "http://");
564 /* if we do handle cookies, ask our handler for them */
565 webkit_web_view_load_uri (web_view, newuri->str);
566 g_string_free (newuri, TRUE);
571 run_js (WebKitWebView * web_view, GArray *argv) {
572 if (argv_idx(argv, 0))
573 webkit_web_view_execute_script (web_view, argv_idx(argv, 0));
577 run_external_js (WebKitWebView * web_view, GArray *argv) {
578 if (argv_idx(argv, 0)) {
579 GArray* lines = read_file_by_line (argv_idx (argv, 0));
584 while ((line = g_array_index(lines, gchar*, i))) {
586 js = g_strdup (line);
588 gchar* newjs = g_strconcat (js, line, NULL);
595 if (uzbl.state.verbose)
596 printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
598 if (argv_idx (argv, 1)) {
599 gchar* newjs = str_replace("%s", argv_idx (argv, 1), js);
603 webkit_web_view_execute_script (web_view, js);
605 g_array_free (lines, TRUE);
610 search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
611 if (argv_idx(argv, 0) && (*argv_idx(argv, 0) != '\0'))
612 uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
614 if (uzbl.state.searchtx != NULL) {
615 if (uzbl.state.verbose)
616 printf ("Searching: %s\n", uzbl.state.searchtx);
618 if (g_strcmp0 (uzbl.state.searchtx, uzbl.state.searchold) != 0) {
619 webkit_web_view_unmark_text_matches (page);
620 webkit_web_view_mark_text_matches (page, uzbl.state.searchtx, FALSE, 0);
622 if (uzbl.state.searchold != NULL)
623 g_free (uzbl.state.searchold);
625 uzbl.state.searchold = g_strdup (uzbl.state.searchtx);
628 webkit_web_view_set_highlight_text_matches (page, TRUE);
629 webkit_web_view_search_text (page, uzbl.state.searchtx, FALSE, forward, TRUE);
630 g_free(uzbl.state.searchtx);
631 uzbl.state.searchtx = NULL;
636 search_forward_text (WebKitWebView *page, GArray *argv) {
637 search_text(page, argv, TRUE);
641 search_reverse_text (WebKitWebView *page, GArray *argv) {
642 search_text(page, argv, FALSE);
646 new_window_load_uri (const gchar * uri) {
647 GString* to_execute = g_string_new ("");
648 g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
650 for (i = 0; entries[i].long_name != NULL; i++) {
651 if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0) && (strcmp(entries[i].long_name,"name")!=0)) {
652 gchar** str = (gchar**)entries[i].arg_data;
654 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
658 if (uzbl.state.verbose)
659 printf("\n%s\n", to_execute->str);
660 g_spawn_command_line_async (to_execute->str, NULL);
661 g_string_free (to_execute, TRUE);
665 close_uzbl (WebKitWebView *page, GArray *argv) {
671 /* --Statusbar functions-- */
673 build_progressbar_ascii(int percent) {
674 int width=uzbl.gui.sbar.progress_w;
677 GString *bar = g_string_new("");
679 l = (double)percent*((double)width/100.);
680 l = (int)(l+.5)>=(int)l ? l+.5 : l;
682 for(i=0; i<(int)l; i++)
683 g_string_append(bar, uzbl.gui.sbar.progress_s);
686 g_string_append(bar, uzbl.gui.sbar.progress_u);
688 return g_string_free(bar, FALSE);
693 const GScannerConfig scan_config = {
696 ) /* cset_skip_characters */,
701 ) /* cset_identifier_first */,
708 ) /* cset_identifier_nth */,
709 ( "" ) /* cpair_comment_single */,
711 TRUE /* case_sensitive */,
713 FALSE /* skip_comment_multi */,
714 FALSE /* skip_comment_single */,
715 FALSE /* scan_comment_multi */,
716 TRUE /* scan_identifier */,
717 TRUE /* scan_identifier_1char */,
718 FALSE /* scan_identifier_NULL */,
719 TRUE /* scan_symbols */,
720 FALSE /* scan_binary */,
721 FALSE /* scan_octal */,
722 FALSE /* scan_float */,
723 FALSE /* scan_hex */,
724 FALSE /* scan_hex_dollar */,
725 FALSE /* scan_string_sq */,
726 FALSE /* scan_string_dq */,
727 TRUE /* numbers_2_int */,
728 FALSE /* int_2_float */,
729 FALSE /* identifier_2_string */,
730 FALSE /* char_2_token */,
731 FALSE /* symbol_2_token */,
732 TRUE /* scope_0_fallback */,
737 uzbl.scan = g_scanner_new(&scan_config);
738 while(symp->symbol_name) {
739 g_scanner_scope_add_symbol(uzbl.scan, 0,
741 GINT_TO_POINTER(symp->symbol_token));
747 expand_template(const char *template) {
748 if(!template) return NULL;
750 GTokenType token = G_TOKEN_NONE;
751 GString *ret = g_string_new("");
755 g_scanner_input_text(uzbl.scan, template, strlen(template));
756 while(!g_scanner_eof(uzbl.scan) && token != G_TOKEN_LAST) {
757 token = g_scanner_get_next_token(uzbl.scan);
759 if(token == G_TOKEN_SYMBOL) {
760 sym = (int)g_scanner_cur_value(uzbl.scan).v_symbol;
763 buf = uzbl.state.uri?
764 g_markup_printf_escaped("%s", uzbl.state.uri) :
766 g_string_append(ret, buf);
770 buf = itos(uzbl.gui.sbar.load_progress);
771 g_string_append(ret, buf);
774 case SYM_LOADPRGSBAR:
775 buf = build_progressbar_ascii(uzbl.gui.sbar.load_progress);
776 g_string_append(ret, buf);
780 buf = uzbl.gui.main_title?
781 g_markup_printf_escaped("%s", uzbl.gui.main_title) :
783 g_string_append(ret, buf);
786 case SYM_SELECTED_URI:
787 buf = uzbl.state.selected_url?
788 g_markup_printf_escaped("%s", uzbl.state.selected_url) :
790 g_string_append(ret, buf);
794 buf = itos(uzbl.xwin);
796 uzbl.state.instance_name?uzbl.state.instance_name:buf);
800 buf = uzbl.state.keycmd->str?
801 g_markup_printf_escaped("%s", uzbl.state.keycmd->str) :
803 g_string_append(ret, buf);
808 uzbl.behave.insert_mode?"[I]":"[C]");
812 uzbl.gui.sbar.msg?uzbl.gui.sbar.msg:"");
816 buf = itos(WEBKIT_MAJOR_VERSION);
817 g_string_append(ret, buf);
821 buf = itos(WEBKIT_MINOR_VERSION);
822 g_string_append(ret, buf);
826 buf = itos(WEBKIT_MICRO_VERSION);
827 g_string_append(ret, buf);
831 g_string_append(ret, uzbl.state.unameinfo.sysname);
834 g_string_append(ret, uzbl.state.unameinfo.nodename);
837 g_string_append(ret, uzbl.state.unameinfo.release);
840 g_string_append(ret, uzbl.state.unameinfo.version);
843 g_string_append(ret, uzbl.state.unameinfo.machine);
846 g_string_append(ret, ARCH);
850 g_string_append(ret, uzbl.state.unameinfo.domainname);
854 g_string_append(ret, COMMIT);
860 else if(token == G_TOKEN_INT) {
861 buf = itos(g_scanner_cur_value(uzbl.scan).v_int);
862 g_string_append(ret, buf);
865 else if(token == G_TOKEN_IDENTIFIER) {
866 g_string_append(ret, (gchar *)g_scanner_cur_value(uzbl.scan).v_identifier);
868 else if(token == G_TOKEN_CHAR) {
869 g_string_append_c(ret, (gchar)g_scanner_cur_value(uzbl.scan).v_char);
873 return g_string_free(ret, FALSE);
875 /* --End Statusbar functions-- */
878 sharg_append(GArray *a, const gchar *str) {
879 const gchar *s = (str ? str : "");
880 g_array_append_val(a, s);
883 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
885 run_command (const gchar *command, const guint npre, const gchar **args,
886 const gboolean sync, char **stdout) {
887 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
890 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
891 gchar *pid = itos(getpid());
892 gchar *xwin = itos(uzbl.xwin);
894 sharg_append(a, command);
895 for (i = 0; i < npre; i++) /* add n args before the default vars */
896 sharg_append(a, args[i]);
897 sharg_append(a, uzbl.state.config_file);
898 sharg_append(a, pid);
899 sharg_append(a, xwin);
900 sharg_append(a, uzbl.comm.fifo_path);
901 sharg_append(a, uzbl.comm.socket_path);
902 sharg_append(a, uzbl.state.uri);
903 sharg_append(a, uzbl.gui.main_title);
905 for (i = npre; i < g_strv_length((gchar**)args); i++)
906 sharg_append(a, args[i]);
909 if (sync) result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
910 NULL, NULL, stdout, NULL, NULL, &err);
911 else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
912 NULL, NULL, NULL, &err);
914 if (uzbl.state.verbose) {
915 GString *s = g_string_new("spawned:");
916 for (i = 0; i < (a->len); i++) {
917 gchar *qarg = g_shell_quote(g_array_index(a, gchar*, i));
918 g_string_append_printf(s, " %s", qarg);
921 g_string_append_printf(s, " -- result: %s", (result ? "true" : "false"));
922 printf("%s\n", s->str);
923 g_string_free(s, TRUE);
926 g_printerr("error on run_command: %s\n", err->message);
931 g_array_free (a, TRUE);
936 split_quoted(const gchar* src, const gboolean unquote) {
937 /* split on unquoted space, return array of strings;
938 remove a layer of quotes and backslashes if unquote */
939 if (!src) return NULL;
943 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
944 GString *s = g_string_new ("");
948 for (p = src; *p != '\0'; p++) {
949 if ((*p == '\\') && unquote) g_string_append_c(s, *++p);
950 else if (*p == '\\') { g_string_append_c(s, *p++);
951 g_string_append_c(s, *p); }
952 else if ((*p == '"') && unquote && !sq) dq = !dq;
953 else if (*p == '"' && !sq) { g_string_append_c(s, *p);
955 else if ((*p == '\'') && unquote && !dq) sq = !sq;
956 else if (*p == '\'' && !dq) { g_string_append_c(s, *p);
958 else if ((*p == ' ') && !dq && !sq) {
959 dup = g_strdup(s->str);
960 g_array_append_val(a, dup);
961 g_string_truncate(s, 0);
962 } else g_string_append_c(s, *p);
964 dup = g_strdup(s->str);
965 g_array_append_val(a, dup);
966 ret = (gchar**)a->data;
967 g_array_free (a, FALSE);
968 g_string_free (s, TRUE);
973 spawn(WebKitWebView *web_view, GArray *argv) {
975 //TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
976 if (argv_idx(argv, 0))
977 run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))), FALSE, NULL);
981 spawn_sh(WebKitWebView *web_view, GArray *argv) {
983 if (!uzbl.behave.shell_cmd) {
984 g_printerr ("spawn_sh: shell_cmd is not set!\n");
989 gchar *spacer = g_strdup("");
990 g_array_insert_val(argv, 1, spacer);
991 gchar **cmd = split_quoted(uzbl.behave.shell_cmd, TRUE);
993 for (i = 1; i < g_strv_length(cmd); i++)
994 g_array_prepend_val(argv, cmd[i]);
996 if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data, FALSE, NULL);
1002 parse_command(const char *cmd, const char *param) {
1005 if ((c = g_hash_table_lookup(uzbl.behave.commands, cmd))) {
1008 gchar **par = split_quoted(param, TRUE);
1009 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1011 if (c[1] == NOSPLIT) { /* don't split */
1012 sharg_append(a, param);
1014 for (i = 0; i < g_strv_length(par); i++)
1015 sharg_append(a, par[i]);
1017 c[0](uzbl.gui.web_view, a);
1019 g_array_free (a, TRUE);
1022 g_printerr ("command \"%s\" not understood. ignoring.\n", cmd);
1025 /* command parser */
1028 uzbl.comm.get_regex = g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
1029 G_REGEX_OPTIMIZE, 0, NULL);
1030 uzbl.comm.set_regex = g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
1031 G_REGEX_OPTIMIZE, 0, NULL);
1032 uzbl.comm.bind_regex = g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
1033 G_REGEX_UNGREEDY|G_REGEX_OPTIMIZE, 0, NULL);
1034 uzbl.comm.act_regex = g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
1035 G_REGEX_OPTIMIZE, 0, NULL);
1036 uzbl.comm.keycmd_regex = g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
1037 G_REGEX_OPTIMIZE, 0, NULL);
1041 get_var_value(gchar *name) {
1044 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1045 if(c->type == TYPE_STRING)
1046 printf("VAR: %s VALUE: %s\n", name, (char *)*c->ptr);
1047 else if(c->type == TYPE_INT)
1048 printf("VAR: %s VALUE: %d\n", name, (int)*c->ptr);
1057 if(*uzbl.net.proxy_url == ' '
1058 || uzbl.net.proxy_url == NULL) {
1059 soup_session_remove_feature_by_type(uzbl.net.soup_session,
1060 (GType) SOUP_SESSION_PROXY_URI);
1063 suri = soup_uri_new(uzbl.net.proxy_url);
1064 g_object_set(G_OBJECT(uzbl.net.soup_session),
1065 SOUP_SESSION_PROXY_URI,
1067 soup_uri_free(suri);
1074 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1075 g_array_append_val (a, uzbl.state.uri);
1076 load_uri(uzbl.gui.web_view, a);
1077 g_array_free (a, TRUE);
1081 cmd_always_insert_mode() {
1082 uzbl.behave.insert_mode =
1083 uzbl.behave.always_insert_mode ? TRUE : FALSE;
1089 g_object_set(G_OBJECT(uzbl.net.soup_session),
1090 SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL);
1094 cmd_max_conns_host() {
1095 g_object_set(G_OBJECT(uzbl.net.soup_session),
1096 SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL);
1101 soup_session_remove_feature
1102 (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1103 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
1104 /*g_free(uzbl.net.soup_logger);*/
1106 uzbl.net.soup_logger = soup_logger_new(uzbl.behave.http_debug, -1);
1107 soup_session_add_feature(uzbl.net.soup_session,
1108 SOUP_SESSION_FEATURE(uzbl.net.soup_logger));
1112 cmd_default_font_size() {
1113 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1114 g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.default_font_size, NULL);
1118 cmd_minimum_font_size() {
1119 WebKitWebSettings *ws = webkit_web_view_get_settings(uzbl.gui.web_view);
1120 g_object_set (G_OBJECT(ws), "minimum-font-size", uzbl.behave.minimum_font_size, NULL);
1127 buf = init_fifo(uzbl.behave.fifo_dir);
1128 if(uzbl.behave.fifo_dir)
1129 g_free(uzbl.behave.fifo_dir);
1131 uzbl.behave.fifo_dir = buf?buf:g_strdup("");
1138 buf = init_socket(uzbl.behave.socket_dir);
1139 if(uzbl.behave.socket_dir)
1140 g_free(uzbl.behave.socket_dir);
1142 uzbl.behave.socket_dir = buf?buf:g_strdup("");
1150 buf = g_utf8_strup(uzbl.behave.modkey, -1);
1151 uzbl.behave.modmask = 0;
1153 if(uzbl.behave.modkey)
1154 g_free(uzbl.behave.modkey);
1155 uzbl.behave.modkey = buf;
1157 for (i = 0; modkeys[i].key != NULL; i++) {
1158 if (g_strrstr(buf, modkeys[i].key))
1159 uzbl.behave.modmask |= modkeys[i].mask;
1167 buf = set_useragent(uzbl.net.useragent);
1168 if(uzbl.net.useragent)
1169 g_free(uzbl.net.useragent);
1171 uzbl.net.useragent = buf?buf:g_strdup("");
1176 gtk_widget_ref(uzbl.gui.scrolled_win);
1177 gtk_widget_ref(uzbl.gui.mainbar);
1178 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win);
1179 gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.mainbar);
1181 if(uzbl.behave.status_top) {
1182 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1183 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1186 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1187 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1189 gtk_widget_unref(uzbl.gui.scrolled_win);
1190 gtk_widget_unref(uzbl.gui.mainbar);
1191 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1196 set_var_value(gchar *name, gchar *val) {
1197 uzbl_cmdprop *c = NULL;
1200 if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
1201 /* check for the variable type */
1202 if (c->type == TYPE_STRING) {
1204 *c->ptr = g_strdup(val);
1205 } else if(c->type == TYPE_INT) {
1206 int *ip = GPOINTER_TO_INT(c->ptr);
1207 *ip = (int)strtoul(val, &endp, 10);
1210 /* invoke a command specific function */
1211 if(c->func) c->func();
1217 runcmd(WebKitWebView* page, GArray *argv) {
1219 parse_cmd_line(argv_idx(argv, 0));
1223 parse_cmd_line(const char *ctl_line) {
1227 if(ctl_line[0] == 's' || ctl_line[0] == 'S') {
1228 tokens = g_regex_split(uzbl.comm.set_regex, ctl_line, 0);
1229 if(tokens[0][0] == 0) {
1230 gchar* value = parseenv (tokens[2]);
1231 set_var_value(tokens[1], value);
1236 printf("Error in command: %s\n", tokens[0]);
1239 else if(ctl_line[0] == 'g' || ctl_line[0] == 'G') {
1240 tokens = g_regex_split(uzbl.comm.get_regex, ctl_line, 0);
1241 if(tokens[0][0] == 0) {
1242 get_var_value(tokens[1]);
1246 printf("Error in command: %s\n", tokens[0]);
1249 else if(ctl_line[0] == 'b' || ctl_line[0] == 'B') {
1250 tokens = g_regex_split(uzbl.comm.bind_regex, ctl_line, 0);
1251 if(tokens[0][0] == 0) {
1252 gchar* value = parseenv (tokens[2]);
1253 add_binding(tokens[1], value);
1258 printf("Error in command: %s\n", tokens[0]);
1261 else if(ctl_line[0] == 'A' || ctl_line[0] == 'a') {
1262 tokens = g_regex_split(uzbl.comm.act_regex, ctl_line, 0);
1263 if(tokens[0][0] == 0) {
1264 parse_command(tokens[1], tokens[2]);
1268 printf("Error in command: %s\n", tokens[0]);
1270 /* KEYCMD command */
1271 else if(ctl_line[0] == 'K' || ctl_line[0] == 'k') {
1272 tokens = g_regex_split(uzbl.comm.keycmd_regex, ctl_line, 0);
1273 if(tokens[0][0] == 0) {
1274 /* should incremental commands want each individual "keystroke"
1275 sent in a loop or the whole string in one go like now? */
1276 g_string_assign(uzbl.state.keycmd, tokens[1]);
1278 if (g_strstr_len(ctl_line, 7, "n") || g_strstr_len(ctl_line, 7, "N"))
1285 else if( (ctl_line[0] == '#')
1286 || (ctl_line[0] == ' ')
1287 || (ctl_line[0] == '\n'))
1288 ; /* ignore these lines */
1290 printf("Command not understood (%s)\n", ctl_line);
1296 build_stream_name(int type, const gchar* dir) {
1298 State *s = &uzbl.state;
1301 xwin_str = itos((int)uzbl.xwin);
1303 str = g_strdup_printf
1304 ("%s/uzbl_fifo_%s", dir,
1305 s->instance_name ? s->instance_name : xwin_str);
1306 } else if (type == SOCKET) {
1307 str = g_strdup_printf
1308 ("%s/uzbl_socket_%s", dir,
1309 s->instance_name ? s->instance_name : xwin_str );
1316 control_fifo(GIOChannel *gio, GIOCondition condition) {
1317 if (uzbl.state.verbose)
1318 printf("triggered\n");
1323 if (condition & G_IO_HUP)
1324 g_error ("Fifo: Read end of pipe died!\n");
1327 g_error ("Fifo: GIOChannel broke\n");
1329 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err);
1330 if (ret == G_IO_STATUS_ERROR) {
1331 g_error ("Fifo: Error reading: %s\n", err->message);
1335 parse_cmd_line(ctl_line);
1342 init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1343 if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */
1344 if (unlink(uzbl.comm.fifo_path) == -1)
1345 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path);
1346 g_free(uzbl.comm.fifo_path);
1347 uzbl.comm.fifo_path = NULL;
1350 if (*dir == ' ') { /* space unsets the variable */
1354 GIOChannel *chan = NULL;
1355 GError *error = NULL;
1356 gchar *path = build_stream_name(FIFO, dir);
1358 if (!file_exists(path)) {
1359 if (mkfifo (path, 0666) == 0) {
1360 // 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.
1361 chan = g_io_channel_new_file(path, "r+", &error);
1363 if (g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, NULL)) {
1364 if (uzbl.state.verbose)
1365 printf ("init_fifo: created successfully as %s\n", path);
1366 uzbl.comm.fifo_path = path;
1368 } else g_warning ("init_fifo: could not add watch on %s\n", path);
1369 } else g_warning ("init_fifo: can't open: %s\n", error->message);
1370 } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno));
1371 } else g_warning ("init_fifo: can't create %s: file exists\n", path);
1373 /* if we got this far, there was an error; cleanup */
1374 if (error) g_error_free (error);
1380 control_stdin(GIOChannel *gio, GIOCondition condition) {
1382 gchar *ctl_line = NULL;
1385 ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, NULL);
1386 if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
1389 parse_cmd_line(ctl_line);
1397 GIOChannel *chan = NULL;
1398 GError *error = NULL;
1400 chan = g_io_channel_unix_new(fileno(stdin));
1402 if (!g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_stdin, NULL)) {
1403 g_error ("Stdin: could not add watch\n");
1405 if (uzbl.state.verbose)
1406 printf ("Stdin: watch added successfully\n");
1409 g_error ("Stdin: Error while opening: %s\n", error->message);
1411 if (error) g_error_free (error);
1415 control_socket(GIOChannel *chan) {
1416 struct sockaddr_un remote;
1417 char buffer[512], *ctl_line;
1419 int sock, clientsock, n, done;
1422 sock = g_io_channel_unix_get_fd(chan);
1424 memset (buffer, 0, sizeof (buffer));
1426 t = sizeof (remote);
1427 clientsock = accept (sock, (struct sockaddr *) &remote, &t);
1431 memset (temp, 0, sizeof (temp));
1432 n = recv (clientsock, temp, 128, 0);
1434 buffer[strlen (buffer)] = '\0';
1438 strcat (buffer, temp);
1441 if (strcmp (buffer, "\n") < 0) {
1442 buffer[strlen (buffer) - 1] = '\0';
1444 buffer[strlen (buffer)] = '\0';
1447 ctl_line = g_strdup(buffer);
1448 parse_cmd_line (ctl_line);
1451 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1452 GError *error = NULL;
1455 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1456 if (ret == G_IO_STATUS_ERROR)
1457 g_error ("Error reading: %s\n", error->message);
1459 printf("Got line %s (%u bytes) \n",ctl_line, len);
1461 parse_line(ctl_line);
1469 init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */
1470 if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */
1471 if (unlink(uzbl.comm.socket_path) == -1)
1472 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path);
1473 g_free(uzbl.comm.socket_path);
1474 uzbl.comm.socket_path = NULL;
1482 GIOChannel *chan = NULL;
1484 struct sockaddr_un local;
1485 gchar *path = build_stream_name(SOCKET, dir);
1487 sock = socket (AF_UNIX, SOCK_STREAM, 0);
1489 local.sun_family = AF_UNIX;
1490 strcpy (local.sun_path, path);
1491 unlink (local.sun_path);
1493 len = strlen (local.sun_path) + sizeof (local.sun_family);
1494 if (bind (sock, (struct sockaddr *) &local, len) != -1) {
1495 if (uzbl.state.verbose)
1496 printf ("init_socket: opened in %s\n", path);
1499 if( (chan = g_io_channel_unix_new(sock)) ) {
1500 g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_socket, chan);
1501 uzbl.comm.socket_path = path;
1504 } else g_warning ("init_socket: could not open in %s: %s\n", path, strerror(errno));
1506 /* if we got this far, there was an error; cleanup */
1513 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1514 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1516 // this function may be called very early when the templates are not set (yet), hence the checks
1518 update_title (void) {
1519 Behaviour *b = &uzbl.behave;
1522 if (b->show_status) {
1523 if (b->title_format_short) {
1524 parsed = expand_template(b->title_format_short);
1525 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1528 if (b->status_format) {
1529 parsed = expand_template(b->status_format);
1530 gtk_label_set_markup(GTK_LABEL(uzbl.gui.mainbar_label), parsed);
1533 if (b->status_background) {
1535 gdk_color_parse (b->status_background, &color);
1536 //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)
1537 gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
1540 if (b->title_format_long) {
1541 parsed = expand_template(b->title_format_long);
1542 gtk_window_set_title (GTK_WINDOW(uzbl.gui.main_window), parsed);
1549 key_press_cb (GtkWidget* window, GdkEventKey* event)
1551 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1555 if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
1556 || 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)
1559 /* turn off insert mode (if always_insert_mode is not used) */
1560 if (uzbl.behave.insert_mode && (event->keyval == GDK_Escape)) {
1561 uzbl.behave.insert_mode = uzbl.behave.always_insert_mode;
1566 if (uzbl.behave.insert_mode && (((event->state & uzbl.behave.modmask) != uzbl.behave.modmask) || (!uzbl.behave.modmask)))
1569 if (event->keyval == GDK_Escape) {
1570 g_string_truncate(uzbl.state.keycmd, 0);
1575 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1576 if (event->keyval == GDK_Insert) {
1578 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) {
1579 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY));
1581 str = gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD));
1584 g_string_append (uzbl.state.keycmd, str);
1591 if ((event->keyval == GDK_BackSpace) && (uzbl.state.keycmd->len > 0)) {
1592 g_string_truncate(uzbl.state.keycmd, uzbl.state.keycmd->len - 1);
1596 gboolean key_ret = FALSE;
1597 if ((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
1599 if (!key_ret) g_string_append(uzbl.state.keycmd, event->string);
1601 run_keycmd(key_ret);
1603 if (key_ret) return (!uzbl.behave.insert_mode);
1608 run_keycmd(const gboolean key_ret) {
1609 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1611 if ((action = g_hash_table_lookup(uzbl.bindings, uzbl.state.keycmd->str))) {
1612 g_string_truncate(uzbl.state.keycmd, 0);
1613 parse_command(action->name, action->param);
1617 /* try if it's an incremental keycmd or one that takes args, and run it */
1618 GString* short_keys = g_string_new ("");
1619 GString* short_keys_inc = g_string_new ("");
1621 for (i=0; i<(uzbl.state.keycmd->len); i++) {
1622 g_string_append_c(short_keys, uzbl.state.keycmd->str[i]);
1623 g_string_assign(short_keys_inc, short_keys->str);
1624 g_string_append_c(short_keys, '_');
1625 g_string_append_c(short_keys_inc, '*');
1627 gboolean exec_now = FALSE;
1628 if ((action = g_hash_table_lookup(uzbl.bindings, short_keys->str))) {
1629 if (key_ret) exec_now = TRUE; /* run normal cmds only if return was pressed */
1630 } else if ((action = g_hash_table_lookup(uzbl.bindings, short_keys_inc->str))) {
1631 if (key_ret) { /* just quit the incremental command on return */
1632 g_string_truncate(uzbl.state.keycmd, 0);
1634 } else exec_now = TRUE; /* always exec incr. commands on keys other than return */
1638 GString* parampart = g_string_new (uzbl.state.keycmd->str);
1639 GString* actionname = g_string_new ("");
1640 GString* actionparam = g_string_new ("");
1641 g_string_erase (parampart, 0, i+1);
1643 g_string_printf (actionname, action->name, parampart->str);
1645 g_string_printf (actionparam, action->param, parampart->str);
1646 parse_command(actionname->str, actionparam->str);
1647 g_string_free (actionname, TRUE);
1648 g_string_free (actionparam, TRUE);
1649 g_string_free (parampart, TRUE);
1651 g_string_truncate(uzbl.state.keycmd, 0);
1655 g_string_truncate(short_keys, short_keys->len - 1);
1657 g_string_free (short_keys, TRUE);
1658 g_string_free (short_keys_inc, TRUE);
1665 GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1666 //main_window_ref = g_object_ref(scrolled_window);
1667 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
1669 g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
1670 gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
1672 g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
1673 g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
1674 g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
1675 g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
1676 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (log_history_cb), g->web_view);
1677 g_signal_connect (G_OBJECT (g->web_view), "load-finished", G_CALLBACK (load_finish_cb), g->web_view);
1678 g_signal_connect (G_OBJECT (g->web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), g->web_view);
1679 g_signal_connect (G_OBJECT (g->web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), g->web_view);
1680 g_signal_connect (G_OBJECT (g->web_view), "download-requested", G_CALLBACK (download_cb), g->web_view);
1681 g_signal_connect (G_OBJECT (g->web_view), "create-web-view", G_CALLBACK (create_web_view_cb), g->web_view);
1683 return scrolled_window;
1690 g->mainbar = gtk_hbox_new (FALSE, 0);
1692 /* keep a reference to the bar so we can re-pack it at runtime*/
1693 //sbar_ref = g_object_ref(g->mainbar);
1695 g->mainbar_label = gtk_label_new ("");
1696 gtk_label_set_selectable((GtkLabel *)g->mainbar_label, TRUE);
1697 gtk_label_set_ellipsize(GTK_LABEL(g->mainbar_label), PANGO_ELLIPSIZE_END);
1698 gtk_misc_set_alignment (GTK_MISC(g->mainbar_label), 0, 0);
1699 gtk_misc_set_padding (GTK_MISC(g->mainbar_label), 2, 2);
1700 gtk_box_pack_start (GTK_BOX (g->mainbar), g->mainbar_label, TRUE, TRUE, 0);
1705 GtkWidget* create_window () {
1706 GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1707 gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
1708 gtk_widget_set_name (window, "Uzbl browser");
1709 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
1710 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (key_press_cb), NULL);
1716 run_handler (const gchar *act, const gchar *args) {
1717 char **parts = g_strsplit(act, " ", 2);
1719 else if ((g_strcmp0(parts[0], "spawn") == 0)
1720 || (g_strcmp0(parts[0], "sh") == 0)) {
1722 GString *a = g_string_new ("");
1724 spawnparts = split_quoted(parts[1], FALSE);
1725 g_string_append_printf(a, "%s", spawnparts[0]);
1726 if (args) g_string_append_printf(a, " %s", args); /* append handler args before user args */
1727 for (i = 1; i < g_strv_length(spawnparts); i++) /* user args */
1728 g_string_append_printf(a, " %s", spawnparts[i]);
1729 parse_command(parts[0], a->str);
1730 g_string_free (a, TRUE);
1731 g_strfreev (spawnparts);
1733 parse_command(parts[0], parts[1]);
1738 add_binding (const gchar *key, const gchar *act) {
1739 char **parts = g_strsplit(act, " ", 2);
1746 if (uzbl.state.verbose)
1747 printf ("Binding %-10s : %s\n", key, act);
1748 action = new_action(parts[0], parts[1]);
1750 g_hash_table_replace(uzbl.bindings, g_strdup(key), action);
1755 get_xdg_var (XDG_Var xdg) {
1756 const gchar* actual_value = getenv (xdg.environmental);
1757 const gchar* home = getenv ("HOME");
1759 gchar* return_value = str_replace ("~", home, actual_value);
1761 if (! actual_value || strcmp (actual_value, "") == 0) {
1762 if (xdg.default_value) {
1763 return_value = str_replace ("~", home, xdg.default_value);
1765 return_value = NULL;
1768 return return_value;
1772 find_xdg_file (int xdg_type, char* filename) {
1773 /* xdg_type = 0 => config
1774 xdg_type = 1 => data
1775 xdg_type = 2 => cache*/
1777 gchar* xdgv = get_xdg_var (XDG[xdg_type]);
1778 gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
1781 gchar* temporary_string;
1785 if (! file_exists (temporary_file) && xdg_type != 2) {
1786 buf = get_xdg_var (XDG[3 + xdg_type]);
1787 temporary_string = (char *) strtok_r (buf, ":", &saveptr);
1790 while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
1791 g_free (temporary_file);
1792 temporary_file = g_strconcat (temporary_string, filename, NULL);
1796 //g_free (temporary_string); - segfaults.
1798 if (file_exists (temporary_file)) {
1799 return temporary_file;
1806 State *s = &uzbl.state;
1807 Network *n = &uzbl.net;
1809 for (i = 0; default_config[i].command != NULL; i++) {
1810 parse_cmd_line(default_config[i].command);
1813 if (!s->config_file) {
1814 s->config_file = find_xdg_file (0, "/uzbl/config");
1817 if (s->config_file) {
1818 GArray* lines = read_file_by_line (s->config_file);
1822 while ((line = g_array_index(lines, gchar*, i))) {
1823 parse_cmd_line (line);
1827 g_array_free (lines, TRUE);
1829 if (uzbl.state.verbose)
1830 printf ("No configuration file loaded.\n");
1833 g_signal_connect(n->soup_session, "request-queued", G_CALLBACK(handle_cookies), NULL);
1837 set_useragent(gchar *val) {
1842 gchar *ua = expand_template(val);
1844 g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, ua, NULL);
1848 static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer user_data){
1851 if (!uzbl.behave.cookie_handler) return;
1853 gchar * stdout = NULL;
1854 soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
1855 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1856 gchar *action = g_strdup ("GET");
1857 SoupURI * soup_uri = soup_message_get_uri(msg);
1858 sharg_append(a, action);
1859 sharg_append(a, soup_uri->host);
1860 sharg_append(a, soup_uri->path);
1861 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, TRUE, &stdout); /* TODO: use handler */
1862 //run_handler(uzbl.behave.cookie_handler); /* TODO: global stdout pointer, spawn_sync */
1864 soup_message_headers_replace (msg->request_headers, "Cookie", stdout);
1867 g_array_free(a, TRUE);
1871 save_cookies (SoupMessage *msg, gpointer user_data){
1875 for (ck = soup_cookies_from_response(msg); ck; ck = ck->next){
1876 cookie = soup_cookie_to_set_cookie_header(ck->data);
1877 GArray *a = g_array_new(TRUE, FALSE, sizeof(gchar*));
1878 SoupURI * soup_uri = soup_message_get_uri(msg);
1879 gchar *action = strdup("PUT");
1880 sharg_append(a, action);
1881 sharg_append(a, soup_uri->host);
1882 sharg_append(a, soup_uri->path);
1883 sharg_append(a, cookie);
1884 run_command(uzbl.behave.cookie_handler, 0, (const gchar **) a->data, FALSE, NULL);
1887 g_array_free(a, TRUE);
1893 main (int argc, char* argv[]) {
1894 gtk_init (&argc, &argv);
1895 if (!g_thread_supported ())
1896 g_thread_init (NULL);
1897 uzbl.state.executable_path = g_strdup(argv[0]);
1898 uzbl.state.selected_url = NULL;
1899 uzbl.state.searchtx = NULL;
1901 GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
1902 g_option_context_add_main_entries (context, entries, NULL);
1903 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1904 g_option_context_parse (context, &argc, &argv, NULL);
1905 g_option_context_free(context);
1906 /* initialize hash table */
1907 uzbl.bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
1909 uzbl.net.soup_session = webkit_get_default_session();
1910 uzbl.state.keycmd = g_string_new("");
1912 if(setup_signal(SIGTERM, catch_sigterm) == SIG_ERR)
1913 fprintf(stderr, "uzbl: error hooking SIGTERM\n");
1914 if(setup_signal(SIGINT, catch_sigint) == SIG_ERR)
1915 fprintf(stderr, "uzbl: error hooking SIGINT\n");
1917 if(uname(&uzbl.state.unameinfo) == -1)
1918 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1920 uzbl.gui.sbar.progress_s = g_strdup("=");
1921 uzbl.gui.sbar.progress_u = g_strdup("ยท");
1922 uzbl.gui.sbar.progress_w = 10;
1927 make_var_to_name_hash();
1929 uzbl.gui.vbox = gtk_vbox_new (FALSE, 0);
1931 uzbl.gui.scrolled_win = create_browser();
1934 /* initial packing */
1935 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0);
1936 gtk_box_pack_start (GTK_BOX (uzbl.gui.vbox), uzbl.gui.mainbar, FALSE, TRUE, 0);
1938 uzbl.gui.main_window = create_window ();
1939 gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox);
1942 gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
1943 gtk_widget_show_all (uzbl.gui.main_window);
1944 uzbl.xwin = GDK_WINDOW_XID (GTK_WIDGET (uzbl.gui.main_window)->window);
1946 if (uzbl.state.verbose) {
1947 printf("Uzbl start location: %s\n", argv[0]);
1948 printf("window_id %i\n",(int) uzbl.xwin);
1949 printf("pid %i\n", getpid ());
1950 printf("name: %s\n", uzbl.state.instance_name);
1953 uzbl.gui.scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
1954 uzbl.gui.bar_v = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_v);
1955 uzbl.gui.scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
1956 uzbl.gui.bar_h = gtk_range_get_adjustment((GtkRange*) uzbl.gui.scbar_h);
1957 gtk_widget_set_scroll_adjustments ((GtkWidget*) uzbl.gui.web_view, uzbl.gui.bar_h, uzbl.gui.bar_v);
1961 if (!uzbl.behave.show_status)
1962 gtk_widget_hide(uzbl.gui.mainbar);
1968 if(uzbl.state.uri) {
1969 GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
1970 g_array_append_val(a, uzbl.state.uri);
1971 load_uri (uzbl.gui.web_view, a);
1972 g_array_free (a, TRUE);
1978 return EXIT_SUCCESS;
1981 /* vi: set et ts=4: */