1 /* -*- c-basic-offset: 4; -*- */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3. See LICENSE file.
7 * Copyright (C) 2006, 2007 Apple Inc.
8 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/utsname.h>
44 #include <webkit/webkit.h>
52 #include <sys/socket.h>
54 #include <libsoup/soup.h>
61 /* define names and pointers to all config specific variables */
65 } var_name_to_ptr
[] = {
66 { "uri", (void *)&uzbl
.state
.uri
},
67 { "status_message", (void *)&uzbl
.gui
.sbar
.msg
},
68 { "show_status", (void *)&uzbl
.behave
.show_status
},
69 { "status_top", (void *)&uzbl
.behave
.status_top
},
70 { "status_format", (void *)&uzbl
.behave
.status_format
},
71 { "status_background", (void *)&uzbl
.behave
.status_background
},
72 { "title_format_long", (void *)&uzbl
.behave
.title_format_long
},
73 { "title_format_short", (void *)&uzbl
.behave
.title_format_short
},
74 { "insert_mode", (void *)&uzbl
.behave
.insert_mode
},
75 { "always_insert_mode", (void *)&uzbl
.behave
.always_insert_mode
},
76 { "reset_command_mode", (void *)&uzbl
.behave
.reset_command_mode
},
77 { "modkey" , (void *)&uzbl
.behave
.modkey
},
78 { "load_finish_handler",(void *)&uzbl
.behave
.load_finish_handler
},
79 { "history_handler", (void *)&uzbl
.behave
.history_handler
},
80 { "download_handler", (void *)&uzbl
.behave
.download_handler
},
81 { "cookie_handler", (void *)&uzbl
.behave
.cookie_handler
},
82 { "fifo_dir", (void *)&uzbl
.behave
.fifo_dir
},
83 { "socket_dir", (void *)&uzbl
.behave
.socket_dir
},
84 { "http_debug", (void *)&uzbl
.behave
.http_debug
},
85 { "default_font_size", (void *)&uzbl
.behave
.default_font_size
},
86 { "minimum_font_size", (void *)&uzbl
.behave
.minimum_font_size
},
87 { "shell_cmd", (void *)&uzbl
.behave
.shell_cmd
},
88 { "proxy_url", (void *)&uzbl
.net
.proxy_url
},
89 { "max_conns", (void *)&uzbl
.net
.max_conns
},
90 { "max_conns_host", (void *)&uzbl
.net
.max_conns_host
},
91 { "useragent", (void *)&uzbl
.net
.useragent
},
93 }, *n2v_p
= var_name_to_ptr
;
99 { "SHIFT", GDK_SHIFT_MASK
}, // shift
100 { "LOCK", GDK_LOCK_MASK
}, // capslock or shiftlock, depending on xserver's modmappings
101 { "CONTROL", GDK_CONTROL_MASK
}, // control
102 { "MOD1", GDK_MOD1_MASK
}, // 4th mod - normally alt but depends on modmappings
103 { "MOD2", GDK_MOD2_MASK
}, // 5th mod
104 { "MOD3", GDK_MOD3_MASK
}, // 6th mod
105 { "MOD4", GDK_MOD4_MASK
}, // 7th mod
106 { "MOD5", GDK_MOD5_MASK
}, // 8th mod
107 { "BUTTON1", GDK_BUTTON1_MASK
}, // 1st mouse button
108 { "BUTTON2", GDK_BUTTON2_MASK
}, // 2nd mouse button
109 { "BUTTON3", GDK_BUTTON3_MASK
}, // 3rd mouse button
110 { "BUTTON4", GDK_BUTTON4_MASK
}, // 4th mouse button
111 { "BUTTON5", GDK_BUTTON5_MASK
}, // 5th mouse button
112 { "SUPER", GDK_SUPER_MASK
}, // super (since 2.10)
113 { "HYPER", GDK_HYPER_MASK
}, // hyper (since 2.10)
114 { "META", GDK_META_MASK
}, // meta (since 2.10)
118 /* construct a hash from the var_name_to_ptr array for quick access */
120 make_var_to_name_hash() {
121 uzbl
.comm
.proto_var
= g_hash_table_new(g_str_hash
, g_str_equal
);
123 g_hash_table_insert(uzbl
.comm
.proto_var
, n2v_p
->name
, n2v_p
->ptr
);
128 /* commandline arguments (set initial values for the state variables) */
129 static GOptionEntry entries
[] =
131 { "uri", 'u', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.uri
, "Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
132 { "verbose", 'v', 0, G_OPTION_ARG_NONE
, &uzbl
.state
.verbose
, "Whether to print all messages or just errors.", NULL
},
133 { "name", 'n', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.instance_name
, "Name of the current instance (defaults to Xorg window id)", "NAME" },
134 { "config", 'c', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.config_file
, "Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
135 { NULL
, 0, 0, 0, NULL
, NULL
, NULL
}
138 typedef void (*Command
)(WebKitWebView
*, const char *);
140 /* --- UTILITY FUNCTIONS --- */
146 snprintf(tmp
, sizeof(tmp
), "%i", val
);
147 return g_strdup(tmp
);
151 str_replace (const char* search
, const char* replace
, const char* string
) {
152 return g_strjoinv (replace
, g_strsplit (string
, search
, -1));
156 setup_signal(int signr
, sigfunc
*shandler
) {
157 struct sigaction nh
, oh
;
159 nh
.sa_handler
= shandler
;
160 sigemptyset(&nh
.sa_mask
);
163 if(sigaction(signr
, &nh
, &oh
) < 0)
171 if (uzbl
.behave
.fifo_dir
)
172 unlink (uzbl
.comm
.fifo_path
);
173 if (uzbl
.behave
.socket_dir
)
174 unlink (uzbl
.comm
.socket_path
);
176 g_string_free(uzbl
.state
.keycmd
, TRUE
);
177 g_hash_table_destroy(uzbl
.bindings
);
178 g_hash_table_destroy(uzbl
.behave
.commands
);
182 /* --- SIGNAL HANDLER --- */
185 catch_sigterm(int s
) {
191 catch_sigint(int s
) {
197 /* --- CALLBACKS --- */
200 new_window_cb (WebKitWebView
*web_view
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*navigation_action
, WebKitWebPolicyDecision
*policy_decision
, gpointer user_data
) {
203 (void) navigation_action
;
204 (void) policy_decision
;
206 const gchar
* uri
= webkit_network_request_get_uri (request
);
207 if (uzbl
.state
.verbose
)
208 printf("New window requested -> %s \n", uri
);
209 new_window_load_uri(uri
);
214 create_web_view_cb (WebKitWebView
*web_view
, WebKitWebFrame
*frame
, gpointer user_data
) {
218 if (uzbl
.state
.selected_url
[0]!=0) {
219 if (uzbl
.state
.verbose
)
220 printf("\nNew web view -> %s\n",uzbl
.state
.selected_url
);
221 new_window_load_uri(uzbl
.state
.selected_url
);
223 if (uzbl
.state
.verbose
)
224 printf("New web view -> %s\n","Nothing to open, exiting");
230 download_cb (WebKitWebView
*web_view
, GObject
*download
, gpointer user_data
) {
233 if (uzbl
.behave
.download_handler
) {
234 const gchar
* uri
= webkit_download_get_uri ((WebKitDownload
*)download
);
235 if (uzbl
.state
.verbose
)
236 printf("Download -> %s\n",uri
);
237 run_command(uzbl
.behave
.download_handler
, uri
, FALSE
, NULL
);
242 /* scroll a bar in a given direction */
244 scroll (GtkAdjustment
* bar
, const char *param
) {
248 amount
= g_ascii_strtod(param
, &end
);
251 fprintf(stderr
, "found something after double: %s\n", end
);
253 gtk_adjustment_set_value (bar
, gtk_adjustment_get_value(bar
)+amount
);
256 static void scroll_begin(WebKitWebView
* page
, const char *param
) {
257 (void) page
; (void) param
;
258 gtk_adjustment_set_value (uzbl
.gui
.bar_v
, gtk_adjustment_get_lower(uzbl
.gui
.bar_v
));
261 static void scroll_end(WebKitWebView
* page
, const char *param
) {
262 (void) page
; (void) param
;
263 gtk_adjustment_set_value (uzbl
.gui
.bar_v
, gtk_adjustment_get_upper(uzbl
.gui
.bar_v
) -
264 gtk_adjustment_get_page_size(uzbl
.gui
.bar_v
));
267 static void scroll_vert(WebKitWebView
* page
, const char *param
) {
269 scroll(uzbl
.gui
.bar_v
, param
);
272 static void scroll_horz(WebKitWebView
* page
, const char *param
) {
274 scroll(uzbl
.gui
.bar_h
, param
);
279 if (!uzbl
.behave
.show_status
) {
280 gtk_widget_hide(uzbl
.gui
.mainbar
);
282 gtk_widget_show(uzbl
.gui
.mainbar
);
288 toggle_status_cb (WebKitWebView
* page
, const char *param
) {
292 if (uzbl
.behave
.show_status
) {
293 gtk_widget_hide(uzbl
.gui
.mainbar
);
295 gtk_widget_show(uzbl
.gui
.mainbar
);
297 uzbl
.behave
.show_status
= !uzbl
.behave
.show_status
;
302 link_hover_cb (WebKitWebView
* page
, const gchar
* title
, const gchar
* link
, gpointer data
) {
306 //Set selected_url state variable
307 uzbl
.state
.selected_url
[0] = '\0';
309 strcpy (uzbl
.state
.selected_url
, link
);
315 title_change_cb (WebKitWebView
* web_view
, WebKitWebFrame
* web_frame
, const gchar
* title
, gpointer data
) {
319 if (uzbl
.gui
.main_title
)
320 g_free (uzbl
.gui
.main_title
);
321 uzbl
.gui
.main_title
= g_strdup (title
);
326 progress_change_cb (WebKitWebView
* page
, gint progress
, gpointer data
) {
329 uzbl
.gui
.sbar
.load_progress
= progress
;
334 load_finish_cb (WebKitWebView
* page
, WebKitWebFrame
* frame
, gpointer data
) {
338 if (uzbl
.behave
.load_finish_handler
) {
339 run_command(uzbl
.behave
.load_finish_handler
, NULL
, FALSE
, NULL
);
344 load_commit_cb (WebKitWebView
* page
, WebKitWebFrame
* frame
, gpointer data
) {
347 free (uzbl
.state
.uri
);
348 GString
* newuri
= g_string_new (webkit_web_frame_get_uri (frame
));
349 uzbl
.state
.uri
= g_string_free (newuri
, FALSE
);
350 if (uzbl
.behave
.reset_command_mode
&& uzbl
.behave
.insert_mode
) {
351 uzbl
.behave
.insert_mode
= uzbl
.behave
.always_insert_mode
;
354 g_string_truncate(uzbl
.state
.keycmd
, 0); // don't need old commands to remain on new page?
358 destroy_cb (GtkWidget
* widget
, gpointer data
) {
366 if (uzbl
.behave
.history_handler
) {
368 struct tm
* timeinfo
;
371 timeinfo
= localtime ( &rawtime
);
372 strftime (date
, 80, "%Y-%m-%d %H:%M:%S", timeinfo
);
373 GString
* args
= g_string_new ("");
374 g_string_printf (args
, "'%s'", date
);
375 run_command(uzbl
.behave
.history_handler
, args
->str
, FALSE
, NULL
);
376 g_string_free (args
, TRUE
);
381 /* VIEW funcs (little webkit wrappers) */
382 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
384 VIEWFUNC(reload_bypass_cache
)
385 VIEWFUNC(stop_loading
)
392 /* -- command to callback/function map for things we cannot attach to any signals */
395 static struct {char *name
; Command command
;} cmdlist
[] =
397 { "back", view_go_back
},
398 { "forward", view_go_forward
},
399 { "scroll_vert", scroll_vert
},
400 { "scroll_horz", scroll_horz
},
401 { "scroll_begin", scroll_begin
},
402 { "scroll_end", scroll_end
},
403 { "reload", view_reload
, },
404 { "reload_ign_cache", view_reload_bypass_cache
},
405 { "stop", view_stop_loading
, },
406 { "zoom_in", view_zoom_in
, }, //Can crash (when max zoom reached?).
407 { "zoom_out", view_zoom_out
, },
409 { "script", run_js
},
410 { "toggle_status", toggle_status_cb
},
413 { "exit", close_uzbl
},
414 { "search", search_forward_text
},
415 { "search_reverse", search_reverse_text
},
416 { "insert_mode", set_insert_mode
},
424 uzbl
.behave
.commands
= g_hash_table_new(g_str_hash
, g_str_equal
);
426 for (i
= 0; i
< LENGTH(cmdlist
); i
++)
427 g_hash_table_insert(uzbl
.behave
.commands
, cmdlist
[i
].name
, cmdlist
[i
].command
);
430 /* -- CORE FUNCTIONS -- */
433 free_action(gpointer act
) {
434 Action
*action
= (Action
*)act
;
435 g_free(action
->name
);
437 g_free(action
->param
);
442 new_action(const gchar
*name
, const gchar
*param
) {
443 Action
*action
= g_new(Action
, 1);
445 action
->name
= g_strdup(name
);
447 action
->param
= g_strdup(param
);
449 action
->param
= NULL
;
455 file_exists (const char * filename
) {
456 return (access(filename
, F_OK
) == 0);
460 set_insert_mode(WebKitWebView
*page
, const gchar
*param
) {
464 uzbl
.behave
.insert_mode
= TRUE
;
469 load_uri (WebKitWebView
* web_view
, const gchar
*param
) {
471 GString
* newuri
= g_string_new (param
);
472 if (g_strrstr (param
, "://") == NULL
)
473 g_string_prepend (newuri
, "http://");
474 /* if we do handle cookies, ask our handler for them */
475 webkit_web_view_load_uri (web_view
, newuri
->str
);
476 g_string_free (newuri
, TRUE
);
481 run_js (WebKitWebView
* web_view
, const gchar
*param
) {
483 webkit_web_view_execute_script (web_view
, param
);
487 search_text (WebKitWebView
*page
, const char *param
, const gboolean forward
) {
488 if ((param
) && (param
[0] != '\0')) {
489 strcpy(uzbl
.state
.searchtx
, param
);
491 if (uzbl
.state
.searchtx
[0] != '\0') {
492 if (uzbl
.state
.verbose
)
493 printf ("Searching: %s\n", uzbl
.state
.searchtx
);
494 webkit_web_view_unmark_text_matches (page
);
495 webkit_web_view_mark_text_matches (page
, uzbl
.state
.searchtx
, FALSE
, 0);
496 webkit_web_view_set_highlight_text_matches (page
, TRUE
);
497 webkit_web_view_search_text (page
, uzbl
.state
.searchtx
, FALSE
, forward
, TRUE
);
502 search_forward_text (WebKitWebView
*page
, const char *param
) {
503 search_text(page
, param
, TRUE
);
507 search_reverse_text (WebKitWebView
*page
, const char *param
) {
508 search_text(page
, param
, FALSE
);
512 new_window_load_uri (const gchar
* uri
) {
513 GString
* to_execute
= g_string_new ("");
514 g_string_append_printf (to_execute
, "%s --uri '%s'", uzbl
.state
.executable_path
, uri
);
516 for (i
= 0; entries
[i
].long_name
!= NULL
; i
++) {
517 if ((entries
[i
].arg
== G_OPTION_ARG_STRING
) && (strcmp(entries
[i
].long_name
,"uri")!=0) && (strcmp(entries
[i
].long_name
,"name")!=0)) {
518 gchar
** str
= (gchar
**)entries
[i
].arg_data
;
520 g_string_append_printf (to_execute
, " --%s '%s'", entries
[i
].long_name
, *str
);
524 if (uzbl
.state
.verbose
)
525 printf("\n%s\n", to_execute
->str
);
526 g_spawn_command_line_async (to_execute
->str
, NULL
);
527 g_string_free (to_execute
, TRUE
);
531 close_uzbl (WebKitWebView
*page
, const char *param
) {
537 /* --Statusbar functions-- */
539 build_progressbar_ascii(int percent
) {
543 GString
*bar
= g_string_new("");
545 l
= (double)percent
*((double)width
/100.);
546 l
= (int)(l
+.5)>=(int)l
? l
+.5 : l
;
548 for(i
=0; i
<(int)l
; i
++)
549 g_string_append(bar
, "=");
552 g_string_append(bar
, "·");
554 return g_string_free(bar
, FALSE
);
559 const GScannerConfig scan_config
= {
562 ) /* cset_skip_characters */,
567 ) /* cset_identifier_first */,
574 ) /* cset_identifier_nth */,
575 ( "" ) /* cpair_comment_single */,
577 TRUE
/* case_sensitive */,
579 FALSE
/* skip_comment_multi */,
580 FALSE
/* skip_comment_single */,
581 FALSE
/* scan_comment_multi */,
582 TRUE
/* scan_identifier */,
583 TRUE
/* scan_identifier_1char */,
584 FALSE
/* scan_identifier_NULL */,
585 TRUE
/* scan_symbols */,
586 FALSE
/* scan_binary */,
587 FALSE
/* scan_octal */,
588 FALSE
/* scan_float */,
589 FALSE
/* scan_hex */,
590 FALSE
/* scan_hex_dollar */,
591 FALSE
/* scan_string_sq */,
592 FALSE
/* scan_string_dq */,
593 TRUE
/* numbers_2_int */,
594 FALSE
/* int_2_float */,
595 FALSE
/* identifier_2_string */,
596 FALSE
/* char_2_token */,
597 FALSE
/* symbol_2_token */,
598 TRUE
/* scope_0_fallback */,
603 uzbl
.scan
= g_scanner_new(&scan_config
);
604 while(symp
->symbol_name
) {
605 g_scanner_scope_add_symbol(uzbl
.scan
, 0,
607 GINT_TO_POINTER(symp
->symbol_token
));
613 expand_template(const char *template) {
614 if(!template) return NULL
;
616 GTokenType token
= G_TOKEN_NONE
;
617 GString
*ret
= g_string_new("");
621 g_scanner_input_text(uzbl
.scan
, template, strlen(template));
622 while(!g_scanner_eof(uzbl
.scan
) && token
!= G_TOKEN_LAST
) {
623 token
= g_scanner_get_next_token(uzbl
.scan
);
625 if(token
== G_TOKEN_SYMBOL
) {
626 sym
= (int)g_scanner_cur_value(uzbl
.scan
).v_symbol
;
631 g_markup_printf_escaped("%s", uzbl
.state
.uri
):"");
634 buf
= itos(uzbl
.gui
.sbar
.load_progress
);
635 g_string_append(ret
, buf
);
638 case SYM_LOADPRGSBAR
:
639 buf
= build_progressbar_ascii(uzbl
.gui
.sbar
.load_progress
);
640 g_string_append(ret
, buf
);
646 g_markup_printf_escaped("%s", uzbl
.gui
.main_title
):"");
648 case SYM_SELECTED_URI
:
650 uzbl
.state
.selected_url
?
651 g_markup_printf_escaped("%s", uzbl
.state
.selected_url
):"");
654 buf
= itos(uzbl
.xwin
);
656 uzbl
.state
.instance_name
?uzbl
.state
.instance_name
:buf
);
661 uzbl
.state
.keycmd
->str
?
662 g_markup_printf_escaped("%s", uzbl
.state
.keycmd
->str
):"");
666 uzbl
.behave
.insert_mode
?"[I]":"[C]");
670 uzbl
.gui
.sbar
.msg
?uzbl
.gui
.sbar
.msg
:"");
674 buf
= itos(WEBKIT_MAJOR_VERSION
);
675 g_string_append(ret
, buf
);
679 buf
= itos(WEBKIT_MINOR_VERSION
);
680 g_string_append(ret
, buf
);
684 buf
= itos(WEBKIT_MICRO_VERSION
);
685 g_string_append(ret
, buf
);
689 g_string_append(ret
, uzbl
.state
.unameinfo
.sysname
);
692 g_string_append(ret
, uzbl
.state
.unameinfo
.nodename
);
695 g_string_append(ret
, uzbl
.state
.unameinfo
.release
);
698 g_string_append(ret
, uzbl
.state
.unameinfo
.version
);
701 g_string_append(ret
, uzbl
.state
.unameinfo
.machine
);
704 g_string_append(ret
, ARCH
);
708 g_string_append(ret
, uzbl
.state
.unameinfo
.domainname
);
712 g_string_append(ret
, COMMIT
);
718 else if(token
== G_TOKEN_INT
) {
719 buf
= itos(g_scanner_cur_value(uzbl
.scan
).v_int
);
720 g_string_append(ret
, buf
);
723 else if(token
== G_TOKEN_IDENTIFIER
) {
724 g_string_append(ret
, (gchar
*)g_scanner_cur_value(uzbl
.scan
).v_identifier
);
726 else if(token
== G_TOKEN_CHAR
) {
727 g_string_append_c(ret
, (gchar
)g_scanner_cur_value(uzbl
.scan
).v_char
);
731 return g_string_free(ret
, FALSE
);
733 /* --End Statusbar functions-- */
736 // make sure that the args string you pass can properly be interpreted (eg properly escaped against whitespace, quotes etc)
738 run_command (const char *command
, const char *args
, const gboolean sync
, char **stdout
) {
739 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
740 GString
*to_execute
= g_string_new ("");
742 gchar
*cmd
= g_strstrip(g_strdup(command
));
743 gchar
*qcfg
= (uzbl
.state
.config_file
? g_shell_quote(uzbl
.state
.config_file
) : g_strdup("''"));
744 gchar
*qfifo
= (uzbl
.comm
.fifo_path
? g_shell_quote(uzbl
.comm
.fifo_path
) : g_strdup("''"));
745 gchar
*qsock
= (uzbl
.comm
.socket_path
? g_shell_quote(uzbl
.comm
.socket_path
) : g_strdup("''"));
746 gchar
*quri
= (uzbl
.state
.uri
? g_shell_quote(uzbl
.state
.uri
) : g_strdup("''"));
747 gchar
*qtitle
= (uzbl
.gui
.main_title
? g_shell_quote(uzbl
.gui
.main_title
) : g_strdup("''"));
750 g_string_printf (to_execute
, "%s %s '%i' '%i' %s %s",
751 cmd
, qcfg
, (int) getpid(), (int) uzbl
.xwin
, qfifo
, qsock
);
752 g_string_append_printf (to_execute
, " %s %s", quri
, qtitle
);
753 if(args
) g_string_append_printf (to_execute
, " %s", args
);
756 result
= g_spawn_command_line_sync (to_execute
->str
, stdout
, NULL
, NULL
, &err
);
757 } else result
= g_spawn_command_line_async (to_execute
->str
, &err
);
758 if (uzbl
.state
.verbose
)
759 printf("Called %s. Result: %s\n", to_execute
->str
, (result
? "TRUE" : "FALSE" ));
760 g_string_free (to_execute
, TRUE
);
762 g_printerr("error on run_command: %s\n", err
->message
);
776 spawn(WebKitWebView
*web_view
, const char *param
) {
779 TODO: allow more control over argument order so that users can have some arguments before the default ones from run_command, and some after
780 gchar** cmd = g_strsplit(param, " ", 2);
783 args = g_shell_quote(cmd[1]);
786 run_command(cmd[0], args, FALSE, NULL);
792 run_command(param
, NULL
, FALSE
, NULL
);
796 spawn_sh(WebKitWebView
*web_view
, const char *param
) {
798 gchar
*cmd
= g_strdup_printf(uzbl
.behave
.shell_cmd
, param
);
804 parse_command(const char *cmd
, const char *param
) {
807 if ((c
= g_hash_table_lookup(uzbl
.behave
.commands
, cmd
)))
808 c(uzbl
.gui
.web_view
, param
);
810 fprintf (stderr
, "command \"%s\" not understood. ignoring.\n", cmd
);
816 uzbl
.comm
.get_regex
= g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
817 G_REGEX_OPTIMIZE
, 0, NULL
);
818 uzbl
.comm
.set_regex
= g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
819 G_REGEX_OPTIMIZE
, 0, NULL
);
820 uzbl
.comm
.bind_regex
= g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
821 G_REGEX_UNGREEDY
|G_REGEX_OPTIMIZE
, 0, NULL
);
822 uzbl
.comm
.act_regex
= g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
823 G_REGEX_OPTIMIZE
, 0, NULL
);
824 uzbl
.comm
.keycmd_regex
= g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
825 G_REGEX_OPTIMIZE
, 0, NULL
);
829 get_var_value(gchar
*name
) {
832 if( (p
= g_hash_table_lookup(uzbl
.comm
.proto_var
, name
)) ) {
833 if(var_is("status_format", name
)
834 || var_is("useragent", name
)
835 || var_is("title_format_short", name
)
836 || var_is("title_format_long", name
)) {
837 printf("VAR: %s VALUE: %s\n", name
, (char *)*p
);
838 } else printf("VAR: %s VALUE: %d\n", name
, (int)*p
);
847 if(*uzbl
.net
.proxy_url
== ' '
848 || uzbl
.net
.proxy_url
== NULL
) {
849 soup_session_remove_feature_by_type(uzbl
.net
.soup_session
,
850 (GType
) SOUP_SESSION_PROXY_URI
);
853 suri
= soup_uri_new(uzbl
.net
.proxy_url
);
854 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
855 SOUP_SESSION_PROXY_URI
,
865 gtk_widget_ref(uzbl
.gui
.scrolled_win
);
866 gtk_widget_ref(uzbl
.gui
.mainbar
);
867 gtk_container_remove(GTK_CONTAINER(uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
);
868 gtk_container_remove(GTK_CONTAINER(uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
);
870 if(uzbl
.behave
.status_top
) {
871 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
872 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
875 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
876 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
878 gtk_widget_unref(uzbl
.gui
.scrolled_win
);
879 gtk_widget_unref(uzbl
.gui
.mainbar
);
880 gtk_widget_grab_focus (GTK_WIDGET (uzbl
.gui
.web_view
));
884 var_is(const char *x
, const char *y
) {
885 return (strcmp(x
, y
) == 0 ? TRUE
: FALSE
);
889 set_var_value(gchar
*name
, gchar
*val
) {
893 if( (p
= g_hash_table_lookup(uzbl
.comm
.proto_var
, name
)) ) {
894 if(var_is("status_message", name
)
895 || var_is("status_background", name
)
896 || var_is("status_format", name
)
897 || var_is("title_format_long", name
)
898 || var_is("title_format_short", name
)
899 || var_is("load_finish_handler", name
)
900 || var_is("history_handler", name
)
901 || var_is("download_handler", name
)
902 || var_is("cookie_handler", name
)) {
908 else if(var_is("uri", name
)) {
911 load_uri(uzbl
.gui
.web_view
, (const gchar
*)*p
);
913 else if(var_is("proxy_url", name
)) {
918 else if(var_is("fifo_dir", name
)) {
920 *p
= init_fifo(g_strdup(val
));
922 else if(var_is("socket_dir", name
)) {
924 *p
= init_socket(g_strdup(val
));
926 else if(var_is("modkey", name
)) {
929 *p
= g_utf8_strup(val
, -1);
930 uzbl
.behave
.modmask
= 0;
931 for (i
= 0; modkeys
[i
].key
!= NULL
; i
++) {
932 if (g_strrstr(*p
, modkeys
[i
].key
))
933 uzbl
.behave
.modmask
|= modkeys
[i
].mask
;
936 else if(var_is("useragent", name
)) {
938 *p
= set_useragent(g_strdup(val
));
940 else if(var_is("shell_cmd", name
)) {
944 /* variables that take int values */
947 *ip
= (int)strtoul(val
, &endp
, 10);
949 if(var_is("show_status", name
)) {
952 else if(var_is("always_insert_mode", name
)) {
953 uzbl
.behave
.insert_mode
=
954 uzbl
.behave
.always_insert_mode
? TRUE
: FALSE
;
957 else if (var_is("max_conns", name
)) {
958 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
959 SOUP_SESSION_MAX_CONNS
, uzbl
.net
.max_conns
, NULL
);
961 else if (var_is("max_conns_host", name
)) {
962 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
963 SOUP_SESSION_MAX_CONNS_PER_HOST
, uzbl
.net
.max_conns_host
, NULL
);
965 else if (var_is("http_debug", name
)) {
966 //soup_session_remove_feature
967 // (uzbl.net.soup_session, uzbl.net.soup_logger);
968 soup_session_remove_feature
969 (uzbl
.net
.soup_session
, SOUP_SESSION_FEATURE(uzbl
.net
.soup_logger
));
970 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
971 /*g_free(uzbl.net.soup_logger);*/
973 uzbl
.net
.soup_logger
= soup_logger_new(uzbl
.behave
.http_debug
, -1);
974 soup_session_add_feature(uzbl
.net
.soup_session
,
975 SOUP_SESSION_FEATURE(uzbl
.net
.soup_logger
));
977 else if (var_is("status_top", name
)) {
980 else if (var_is("default_font_size", name
)) {
981 WebKitWebSettings
*ws
= webkit_web_view_get_settings(uzbl
.gui
.web_view
);
982 g_object_set (G_OBJECT(ws
), "default-font-size", *ip
, NULL
);
984 else if (var_is("minimum_font_size", name
)) {
985 WebKitWebSettings
*ws
= webkit_web_view_get_settings(uzbl
.gui
.web_view
);
986 g_object_set (G_OBJECT(ws
), "minimum-font-size", *ip
, NULL
);
994 runcmd(WebKitWebView
* page
, const char *param
) {
996 parse_cmd_line(param
);
1000 parse_cmd_line(const char *ctl_line
) {
1004 if(ctl_line
[0] == 's' || ctl_line
[0] == 'S') {
1005 tokens
= g_regex_split(uzbl
.comm
.set_regex
, ctl_line
, 0);
1006 if(tokens
[0][0] == 0) {
1007 set_var_value(tokens
[1], tokens
[2]);
1011 printf("Error in command: %s\n", tokens
[0]);
1014 else if(ctl_line
[0] == 'g' || ctl_line
[0] == 'G') {
1015 tokens
= g_regex_split(uzbl
.comm
.get_regex
, ctl_line
, 0);
1016 if(tokens
[0][0] == 0) {
1017 get_var_value(tokens
[1]);
1021 printf("Error in command: %s\n", tokens
[0]);
1024 else if(ctl_line
[0] == 'b' || ctl_line
[0] == 'B') {
1025 tokens
= g_regex_split(uzbl
.comm
.bind_regex
, ctl_line
, 0);
1026 if(tokens
[0][0] == 0) {
1027 add_binding(tokens
[1], tokens
[2]);
1031 printf("Error in command: %s\n", tokens
[0]);
1034 else if(ctl_line
[0] == 'A' || ctl_line
[0] == 'a') {
1035 tokens
= g_regex_split(uzbl
.comm
.act_regex
, ctl_line
, 0);
1036 if(tokens
[0][0] == 0) {
1037 parse_command(tokens
[1], tokens
[2]);
1041 printf("Error in command: %s\n", tokens
[0]);
1043 /* KEYCMD command */
1044 else if(ctl_line
[0] == 'K' || ctl_line
[0] == 'k') {
1045 tokens
= g_regex_split(uzbl
.comm
.keycmd_regex
, ctl_line
, 0);
1046 if(tokens
[0][0] == 0) {
1047 /* should incremental commands want each individual "keystroke"
1048 sent in a loop or the whole string in one go like now? */
1049 g_string_assign(uzbl
.state
.keycmd
, tokens
[1]);
1056 else if( (ctl_line
[0] == '#')
1057 || (ctl_line
[0] == ' ')
1058 || (ctl_line
[0] == '\n'))
1059 ; /* ignore these lines */
1061 printf("Command not understood (%s)\n", ctl_line
);
1067 build_stream_name(int type
, const gchar
* dir
) {
1069 State
*s
= &uzbl
.state
;
1072 xwin_str
= itos((int)uzbl
.xwin
);
1074 str
= g_strdup_printf
1075 ("%s/uzbl_fifo_%s", dir
,
1076 s
->instance_name
? s
->instance_name
: xwin_str
);
1077 } else if (type
== SOCKET
) {
1078 str
= g_strdup_printf
1079 ("%s/uzbl_socket_%s", dir
,
1080 s
->instance_name
? s
->instance_name
: xwin_str
);
1087 control_fifo(GIOChannel
*gio
, GIOCondition condition
) {
1088 if (uzbl
.state
.verbose
)
1089 printf("triggered\n");
1094 if (condition
& G_IO_HUP
)
1095 g_error ("Fifo: Read end of pipe died!\n");
1098 g_error ("Fifo: GIOChannel broke\n");
1100 ret
= g_io_channel_read_line(gio
, &ctl_line
, NULL
, NULL
, &err
);
1101 if (ret
== G_IO_STATUS_ERROR
) {
1102 g_error ("Fifo: Error reading: %s\n", err
->message
);
1106 parse_cmd_line(ctl_line
);
1113 init_fifo(gchar
*dir
) { /* return dir or, on error, free dir and return NULL */
1114 if (uzbl
.comm
.fifo_path
) { /* get rid of the old fifo if one exists */
1115 if (unlink(uzbl
.comm
.fifo_path
) == -1)
1116 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl
.comm
.fifo_path
);
1117 g_free(uzbl
.comm
.fifo_path
);
1118 uzbl
.comm
.fifo_path
= NULL
;
1121 if (*dir
== ' ') { /* space unsets the variable */
1126 GIOChannel
*chan
= NULL
;
1127 GError
*error
= NULL
;
1128 gchar
*path
= build_stream_name(FIFO
, dir
);
1130 if (!file_exists(path
)) {
1131 if (mkfifo (path
, 0666) == 0) {
1132 // 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.
1133 chan
= g_io_channel_new_file(path
, "r+", &error
);
1135 if (g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_fifo
, NULL
)) {
1136 if (uzbl
.state
.verbose
)
1137 printf ("init_fifo: created successfully as %s\n", path
);
1138 uzbl
.comm
.fifo_path
= path
;
1140 } else g_warning ("init_fifo: could not add watch on %s\n", path
);
1141 } else g_warning ("init_fifo: can't open: %s\n", error
->message
);
1142 } else g_warning ("init_fifo: can't create %s: %s\n", path
, strerror(errno
));
1143 } else g_warning ("init_fifo: can't create %s: file exists\n", path
);
1145 /* if we got this far, there was an error; cleanup */
1146 if (error
) g_error_free (error
);
1153 control_stdin(GIOChannel
*gio
, GIOCondition condition
) {
1154 gchar
*ctl_line
= NULL
;
1155 gsize ctl_line_len
= 0;
1158 if (condition
& G_IO_HUP
) {
1159 ret
= g_io_channel_shutdown (gio
, FALSE
, NULL
);
1163 ret
= g_io_channel_read_line(gio
, &ctl_line
, &ctl_line_len
, NULL
, NULL
);
1164 if ( (ret
== G_IO_STATUS_ERROR
) || (ret
== G_IO_STATUS_EOF
) )
1167 parse_cmd_line(ctl_line
);
1175 GIOChannel
*chan
= NULL
;
1176 GError
*error
= NULL
;
1178 chan
= g_io_channel_unix_new(fileno(stdin
));
1180 if (!g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_stdin
, NULL
)) {
1181 g_error ("Stdin: could not add watch\n");
1183 if (uzbl
.state
.verbose
)
1184 printf ("Stdin: watch added successfully\n");
1187 g_error ("Stdin: Error while opening: %s\n", error
->message
);
1189 if (error
) g_error_free (error
);
1193 control_socket(GIOChannel
*chan
) {
1194 struct sockaddr_un remote
;
1195 char buffer
[512], *ctl_line
;
1197 int sock
, clientsock
, n
, done
;
1200 sock
= g_io_channel_unix_get_fd(chan
);
1202 memset (buffer
, 0, sizeof (buffer
));
1204 t
= sizeof (remote
);
1205 clientsock
= accept (sock
, (struct sockaddr
*) &remote
, &t
);
1209 memset (temp
, 0, sizeof (temp
));
1210 n
= recv (clientsock
, temp
, 128, 0);
1212 buffer
[strlen (buffer
)] = '\0';
1216 strcat (buffer
, temp
);
1219 if (strcmp (buffer
, "\n") < 0) {
1220 buffer
[strlen (buffer
) - 1] = '\0';
1222 buffer
[strlen (buffer
)] = '\0';
1225 ctl_line
= g_strdup(buffer
);
1226 parse_cmd_line (ctl_line
);
1229 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1230 GError *error = NULL;
1233 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1234 if (ret == G_IO_STATUS_ERROR)
1235 g_error ("Error reading: %s\n", error->message);
1237 printf("Got line %s (%u bytes) \n",ctl_line, len);
1239 parse_line(ctl_line);
1247 init_socket(gchar
*dir
) { /* return dir or, on error, free dir and return NULL */
1248 if (uzbl
.comm
.socket_path
) { /* remove an existing socket should one exist */
1249 if (unlink(uzbl
.comm
.socket_path
) == -1)
1250 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl
.comm
.socket_path
);
1251 g_free(uzbl
.comm
.socket_path
);
1252 uzbl
.comm
.socket_path
= NULL
;
1260 GIOChannel
*chan
= NULL
;
1262 struct sockaddr_un local
;
1263 gchar
*path
= build_stream_name(SOCKET
, dir
);
1265 sock
= socket (AF_UNIX
, SOCK_STREAM
, 0);
1267 local
.sun_family
= AF_UNIX
;
1268 strcpy (local
.sun_path
, path
);
1269 unlink (local
.sun_path
);
1271 len
= strlen (local
.sun_path
) + sizeof (local
.sun_family
);
1272 if (bind (sock
, (struct sockaddr
*) &local
, len
) != -1) {
1273 if (uzbl
.state
.verbose
)
1274 printf ("init_socket: opened in %s\n", path
);
1277 if( (chan
= g_io_channel_unix_new(sock
)) ) {
1278 g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_socket
, chan
);
1279 uzbl
.comm
.socket_path
= path
;
1282 } else g_warning ("init_socket: could not open in %s: %s\n", path
, strerror(errno
));
1284 /* if we got this far, there was an error; cleanup */
1291 NOTE: we want to keep variables like b->title_format_long in their "unprocessed" state
1292 it will probably improve performance if we would "cache" the processed variant, but for now it works well enough...
1294 // this function may be called very early when the templates are not set (yet), hence the checks
1296 update_title (void) {
1297 Behaviour
*b
= &uzbl
.behave
;
1300 if (b
->show_status
) {
1301 if (b
->title_format_short
) {
1302 parsed
= expand_template(b
->title_format_short
);
1303 gtk_window_set_title (GTK_WINDOW(uzbl
.gui
.main_window
), parsed
);
1306 if (b
->status_format
) {
1307 parsed
= expand_template(b
->status_format
);
1308 gtk_label_set_markup(GTK_LABEL(uzbl
.gui
.mainbar_label
), parsed
);
1311 if (b
->status_background
) {
1313 gdk_color_parse (b
->status_background
, &color
);
1314 //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)
1315 gtk_widget_modify_bg (uzbl
.gui
.main_window
, GTK_STATE_NORMAL
, &color
);
1318 if (b
->title_format_long
) {
1319 parsed
= expand_template(b
->title_format_long
);
1320 gtk_window_set_title (GTK_WINDOW(uzbl
.gui
.main_window
), parsed
);
1327 key_press_cb (WebKitWebView
* page
, GdkEventKey
* event
)
1329 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1333 if (event
->type
!= GDK_KEY_PRESS
|| event
->keyval
== GDK_Page_Up
|| event
->keyval
== GDK_Page_Down
1334 || 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
)
1337 /* turn off insert mode (if always_insert_mode is not used) */
1338 if (uzbl
.behave
.insert_mode
&& (event
->keyval
== GDK_Escape
)) {
1339 uzbl
.behave
.insert_mode
= uzbl
.behave
.always_insert_mode
;
1344 if (uzbl
.behave
.insert_mode
&& (((event
->state
& uzbl
.behave
.modmask
) != uzbl
.behave
.modmask
) || (!uzbl
.behave
.modmask
)))
1347 if (event
->keyval
== GDK_Escape
) {
1348 g_string_truncate(uzbl
.state
.keycmd
, 0);
1353 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1354 if (event
->keyval
== GDK_Insert
) {
1356 if ((event
->state
& GDK_SHIFT_MASK
) == GDK_SHIFT_MASK
) {
1357 str
= gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY
));
1359 str
= gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
));
1362 g_string_append (uzbl
.state
.keycmd
, str
);
1369 if ((event
->keyval
== GDK_BackSpace
) && (uzbl
.state
.keycmd
->len
> 0)) {
1370 g_string_truncate(uzbl
.state
.keycmd
, uzbl
.state
.keycmd
->len
- 1);
1374 gboolean key_ret
= FALSE
;
1375 if ((event
->keyval
== GDK_Return
) || (event
->keyval
== GDK_KP_Enter
))
1377 if (!key_ret
) g_string_append(uzbl
.state
.keycmd
, event
->string
);
1379 run_keycmd(key_ret
);
1381 if (key_ret
) return (!uzbl
.behave
.insert_mode
);
1386 run_keycmd(const gboolean key_ret
) {
1387 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1389 if ((action
= g_hash_table_lookup(uzbl
.bindings
, uzbl
.state
.keycmd
->str
))) {
1390 g_string_truncate(uzbl
.state
.keycmd
, 0);
1391 parse_command(action
->name
, action
->param
);
1395 /* try if it's an incremental keycmd or one that takes args, and run it */
1396 GString
* short_keys
= g_string_new ("");
1397 GString
* short_keys_inc
= g_string_new ("");
1399 for (i
=0; i
<(uzbl
.state
.keycmd
->len
); i
++) {
1400 g_string_append_c(short_keys
, uzbl
.state
.keycmd
->str
[i
]);
1401 g_string_assign(short_keys_inc
, short_keys
->str
);
1402 g_string_append_c(short_keys
, '_');
1403 g_string_append_c(short_keys_inc
, '*');
1405 gboolean exec_now
= FALSE
;
1406 if ((action
= g_hash_table_lookup(uzbl
.bindings
, short_keys
->str
))) {
1407 if (key_ret
) exec_now
= TRUE
; /* run normal cmds only if return was pressed */
1408 } else if ((action
= g_hash_table_lookup(uzbl
.bindings
, short_keys_inc
->str
))) {
1409 if (key_ret
) { /* just quit the incremental command on return */
1410 g_string_truncate(uzbl
.state
.keycmd
, 0);
1412 } else exec_now
= TRUE
; /* always exec incr. commands on keys other than return */
1416 GString
* parampart
= g_string_new (uzbl
.state
.keycmd
->str
);
1417 GString
* actionname
= g_string_new ("");
1418 GString
* actionparam
= g_string_new ("");
1419 g_string_erase (parampart
, 0, i
+1);
1421 g_string_printf (actionname
, action
->name
, parampart
->str
);
1423 g_string_printf (actionparam
, action
->param
, parampart
->str
);
1424 parse_command(actionname
->str
, actionparam
->str
);
1425 g_string_free (actionname
, TRUE
);
1426 g_string_free (actionparam
, TRUE
);
1427 g_string_free (parampart
, TRUE
);
1429 g_string_truncate(uzbl
.state
.keycmd
, 0);
1433 g_string_truncate(short_keys
, short_keys
->len
- 1);
1435 g_string_free (short_keys
, TRUE
);
1436 g_string_free (short_keys_inc
, TRUE
);
1443 GtkWidget
* scrolled_window
= gtk_scrolled_window_new (NULL
, NULL
);
1444 //main_window_ref = g_object_ref(scrolled_window);
1445 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
1447 g
->web_view
= WEBKIT_WEB_VIEW (webkit_web_view_new ());
1448 gtk_container_add (GTK_CONTAINER (scrolled_window
), GTK_WIDGET (g
->web_view
));
1450 g_signal_connect (G_OBJECT (g
->web_view
), "title-changed", G_CALLBACK (title_change_cb
), g
->web_view
);
1451 g_signal_connect (G_OBJECT (g
->web_view
), "load-progress-changed", G_CALLBACK (progress_change_cb
), g
->web_view
);
1452 g_signal_connect (G_OBJECT (g
->web_view
), "load-committed", G_CALLBACK (load_commit_cb
), g
->web_view
);
1453 g_signal_connect (G_OBJECT (g
->web_view
), "load-finished", G_CALLBACK (log_history_cb
), g
->web_view
);
1454 g_signal_connect (G_OBJECT (g
->web_view
), "load-finished", G_CALLBACK (load_finish_cb
), g
->web_view
);
1455 g_signal_connect (G_OBJECT (g
->web_view
), "hovering-over-link", G_CALLBACK (link_hover_cb
), g
->web_view
);
1456 g_signal_connect (G_OBJECT (g
->web_view
), "key-press-event", G_CALLBACK (key_press_cb
), g
->web_view
);
1457 g_signal_connect (G_OBJECT (g
->web_view
), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb
), g
->web_view
);
1458 g_signal_connect (G_OBJECT (g
->web_view
), "download-requested", G_CALLBACK (download_cb
), g
->web_view
);
1459 g_signal_connect (G_OBJECT (g
->web_view
), "create-web-view", G_CALLBACK (create_web_view_cb
), g
->web_view
);
1461 return scrolled_window
;
1468 g
->mainbar
= gtk_hbox_new (FALSE
, 0);
1470 /* keep a reference to the bar so we can re-pack it at runtime*/
1471 //sbar_ref = g_object_ref(g->mainbar);
1473 g
->mainbar_label
= gtk_label_new ("");
1474 gtk_label_set_selectable((GtkLabel
*)g
->mainbar_label
, TRUE
);
1475 gtk_label_set_ellipsize(GTK_LABEL(g
->mainbar_label
), PANGO_ELLIPSIZE_END
);
1476 gtk_misc_set_alignment (GTK_MISC(g
->mainbar_label
), 0, 0);
1477 gtk_misc_set_padding (GTK_MISC(g
->mainbar_label
), 2, 2);
1478 gtk_box_pack_start (GTK_BOX (g
->mainbar
), g
->mainbar_label
, TRUE
, TRUE
, 0);
1483 GtkWidget
* create_window () {
1484 GtkWidget
* window
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
1485 gtk_window_set_default_size (GTK_WINDOW (window
), 800, 600);
1486 gtk_widget_set_name (window
, "Uzbl browser");
1487 g_signal_connect (G_OBJECT (window
), "destroy", G_CALLBACK (destroy_cb
), NULL
);
1493 add_binding (const gchar
*key
, const gchar
*act
) {
1494 char **parts
= g_strsplit(act
, " ", 2);
1501 if (uzbl
.state
.verbose
)
1502 printf ("Binding %-10s : %s\n", key
, act
);
1503 action
= new_action(parts
[0], parts
[1]);
1505 if(g_hash_table_lookup(uzbl
.bindings
, key
))
1506 g_hash_table_remove(uzbl
.bindings
, key
);
1507 g_hash_table_insert(uzbl
.bindings
, g_strdup(key
), action
);
1513 get_xdg_var (XDG_Var xdg
) {
1514 const gchar
* actual_value
= getenv (xdg
.environmental
);
1515 const gchar
* home
= getenv ("HOME");
1517 gchar
* return_value
= str_replace ("~", home
, g_strdup (actual_value
));
1519 if (! actual_value
|| strcmp (actual_value
, "") == 0) {
1520 if (xdg
.default_value
) {
1521 return_value
= str_replace ("~", home
, g_strdup (xdg
.default_value
));
1523 return_value
= NULL
;
1527 return return_value
;
1531 find_xdg_file (int xdg_type
, char* filename
) {
1532 /* xdg_type = 0 => config
1533 xdg_type = 1 => data
1534 xdg_type = 2 => cache*/
1536 gchar
* temporary_file
= (char *)malloc (1024);
1537 gchar
* temporary_string
= NULL
;
1540 strcpy (temporary_file
, get_xdg_var (XDG
[xdg_type
]));
1542 strcat (temporary_file
, filename
);
1544 if (! file_exists (temporary_file
) && xdg_type
!= 2) {
1545 temporary_string
= (char *) strtok_r (get_xdg_var (XDG
[3 + xdg_type
]), ":", &saveptr
);
1547 while (temporary_string
&& ! file_exists (temporary_file
)) {
1548 strcpy (temporary_file
, temporary_string
);
1549 strcat (temporary_file
, filename
);
1550 temporary_string
= (char * ) strtok_r (NULL
, ":", &saveptr
);
1554 if (file_exists (temporary_file
)) {
1555 return temporary_file
;
1563 State
*s
= &uzbl
.state
;
1564 Network
*n
= &uzbl
.net
;
1566 uzbl
.behave
.reset_command_mode
= 1;
1568 if (!s
->config_file
) {
1569 s
->config_file
= g_strdup (find_xdg_file (0, "/uzbl/config"));
1572 if (s
->config_file
) {
1573 GIOChannel
*chan
= NULL
;
1574 gchar
*readbuf
= NULL
;
1577 chan
= g_io_channel_new_file(s
->config_file
, "r", NULL
);
1580 while (g_io_channel_read_line(chan
, &readbuf
, &len
, NULL
, NULL
)
1581 == G_IO_STATUS_NORMAL
) {
1582 parse_cmd_line(readbuf
);
1586 g_io_channel_unref (chan
);
1587 if (uzbl
.state
.verbose
)
1588 printf ("Config %s loaded\n", s
->config_file
);
1590 fprintf(stderr
, "uzbl: error loading file%s\n", s
->config_file
);
1593 if (uzbl
.state
.verbose
)
1594 printf ("No configuration file loaded.\n");
1596 if (!uzbl
.behave
.status_format
)
1597 set_var_value("status_format", STATUS_DEFAULT
);
1598 if (!uzbl
.behave
.title_format_long
)
1599 set_var_value("title_format_long", TITLE_LONG_DEFAULT
);
1600 if (!uzbl
.behave
.title_format_short
)
1601 set_var_value("title_format_short", TITLE_SHORT_DEFAULT
);
1604 g_signal_connect(n
->soup_session
, "request-queued", G_CALLBACK(handle_cookies
), NULL
);
1608 set_useragent(gchar
*val
) {
1613 gchar
*ua
= expand_template(val
);
1615 g_object_set(G_OBJECT(uzbl
.net
.soup_session
), SOUP_SESSION_USER_AGENT
, ua
, NULL
);
1619 static void handle_cookies (SoupSession
*session
, SoupMessage
*msg
, gpointer user_data
){
1622 if (!uzbl
.behave
.cookie_handler
) return;
1624 gchar
* stdout
= NULL
;
1625 soup_message_add_header_handler(msg
, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies
), NULL
);
1626 GString
* args
= g_string_new ("");
1627 SoupURI
* soup_uri
= soup_message_get_uri(msg
);
1628 g_string_printf (args
, "GET %s %s", soup_uri
->host
, soup_uri
->path
);
1629 run_command(uzbl
.behave
.cookie_handler
, args
->str
, TRUE
, &stdout
);
1631 soup_message_headers_replace (msg
->request_headers
, "Cookie", stdout
);
1633 g_string_free(args
, TRUE
);
1637 save_cookies (SoupMessage
*msg
, gpointer user_data
){
1641 for (ck
= soup_cookies_from_response(msg
); ck
; ck
= ck
->next
){
1642 cookie
= soup_cookie_to_set_cookie_header(ck
->data
);
1643 GString
* args
= g_string_new ("");
1644 SoupURI
* soup_uri
= soup_message_get_uri(msg
);
1645 g_string_printf (args
, "PUT %s %s \"%s\"", soup_uri
->host
, soup_uri
->path
, cookie
);
1646 run_command(uzbl
.behave
.cookie_handler
, args
->str
, FALSE
, NULL
);
1647 g_string_free(args
, TRUE
);
1655 main (int argc
, char* argv
[]) {
1656 gtk_init (&argc
, &argv
);
1657 if (!g_thread_supported ())
1658 g_thread_init (NULL
);
1660 strcpy(uzbl
.state
.executable_path
,argv
[0]);
1662 GOptionContext
* context
= g_option_context_new ("- some stuff here maybe someday");
1663 g_option_context_add_main_entries (context
, entries
, NULL
);
1664 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
1665 g_option_context_parse (context
, &argc
, &argv
, NULL
);
1666 /* initialize hash table */
1667 uzbl
.bindings
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, free_action
);
1669 uzbl
.net
.soup_session
= webkit_get_default_session();
1670 uzbl
.state
.keycmd
= g_string_new("");
1672 if(setup_signal(SIGTERM
, catch_sigterm
) == SIG_ERR
)
1673 fprintf(stderr
, "uzbl: error hooking SIGTERM\n");
1674 if(setup_signal(SIGINT
, catch_sigint
) == SIG_ERR
)
1675 fprintf(stderr
, "uzbl: error hooking SIGINT\n");
1677 if(uname(&uzbl
.state
.unameinfo
) == -1)
1678 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1683 make_var_to_name_hash();
1686 uzbl
.gui
.vbox
= gtk_vbox_new (FALSE
, 0);
1688 uzbl
.gui
.scrolled_win
= create_browser();
1691 /* initial packing */
1692 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
1693 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
1695 uzbl
.gui
.main_window
= create_window ();
1696 gtk_container_add (GTK_CONTAINER (uzbl
.gui
.main_window
), uzbl
.gui
.vbox
);
1698 load_uri (uzbl
.gui
.web_view
, uzbl
.state
.uri
); //TODO: is this needed?
1700 gtk_widget_grab_focus (GTK_WIDGET (uzbl
.gui
.web_view
));
1701 gtk_widget_show_all (uzbl
.gui
.main_window
);
1702 uzbl
.xwin
= GDK_WINDOW_XID (GTK_WIDGET (uzbl
.gui
.main_window
)->window
);
1704 if (uzbl
.state
.verbose
) {
1705 printf("Uzbl start location: %s\n", argv
[0]);
1706 printf("window_id %i\n",(int) uzbl
.xwin
);
1707 printf("pid %i\n", getpid ());
1708 printf("name: %s\n", uzbl
.state
.instance_name
);
1711 uzbl
.gui
.scbar_v
= (GtkScrollbar
*) gtk_vscrollbar_new (NULL
);
1712 uzbl
.gui
.bar_v
= gtk_range_get_adjustment((GtkRange
*) uzbl
.gui
.scbar_v
);
1713 uzbl
.gui
.scbar_h
= (GtkScrollbar
*) gtk_hscrollbar_new (NULL
);
1714 uzbl
.gui
.bar_h
= gtk_range_get_adjustment((GtkRange
*) uzbl
.gui
.scbar_h
);
1715 gtk_widget_set_scroll_adjustments ((GtkWidget
*) uzbl
.gui
.web_view
, uzbl
.gui
.bar_h
, uzbl
.gui
.bar_v
);
1719 if (!uzbl
.behave
.show_status
)
1720 gtk_widget_hide(uzbl
.gui
.mainbar
);
1729 return EXIT_SUCCESS
;
1732 /* vi: set et ts=4: */