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 { "insert_mode", (void *)&uzbl
.behave
.insert_mode
},
73 { "always_insert_mode", (void *)&uzbl
.behave
.always_insert_mode
},
74 { "reset_command_mode", (void *)&uzbl
.behave
.reset_command_mode
},
75 { "modkey" , (void *)&uzbl
.behave
.modkey
},
76 { "load_finish_handler",(void *)&uzbl
.behave
.load_finish_handler
},
77 { "history_handler", (void *)&uzbl
.behave
.history_handler
},
78 { "download_handler", (void *)&uzbl
.behave
.download_handler
},
79 { "cookie_handler", (void *)&uzbl
.behave
.cookie_handler
},
80 { "fifo_dir", (void *)&uzbl
.behave
.fifo_dir
},
81 { "socket_dir", (void *)&uzbl
.behave
.socket_dir
},
82 { "http_debug", (void *)&uzbl
.behave
.http_debug
},
83 { "proxy_url", (void *)&uzbl
.net
.proxy_url
},
84 { "max_conns", (void *)&uzbl
.net
.max_conns
},
85 { "max_conns_host", (void *)&uzbl
.net
.max_conns_host
},
86 { "useragent", (void *)&uzbl
.net
.useragent
},
88 }, *n2v_p
= var_name_to_ptr
;
94 { "SHIFT", GDK_SHIFT_MASK
}, // shift
95 { "LOCK", GDK_LOCK_MASK
}, // capslock or shiftlock, depending on xserver's modmappings
96 { "CONTROL", GDK_CONTROL_MASK
}, // control
97 { "MOD1", GDK_MOD1_MASK
}, // 4th mod - normally alt but depends on modmappings
98 { "MOD2", GDK_MOD2_MASK
}, // 5th mod
99 { "MOD3", GDK_MOD3_MASK
}, // 6th mod
100 { "MOD4", GDK_MOD4_MASK
}, // 7th mod
101 { "MOD5", GDK_MOD5_MASK
}, // 8th mod
102 { "BUTTON1", GDK_BUTTON1_MASK
}, // 1st mouse button
103 { "BUTTON2", GDK_BUTTON2_MASK
}, // 2nd mouse button
104 { "BUTTON3", GDK_BUTTON3_MASK
}, // 3rd mouse button
105 { "BUTTON4", GDK_BUTTON4_MASK
}, // 4th mouse button
106 { "BUTTON5", GDK_BUTTON5_MASK
}, // 5th mouse button
107 { "SUPER", GDK_SUPER_MASK
}, // super (since 2.10)
108 { "HYPER", GDK_HYPER_MASK
}, // hyper (since 2.10)
109 { "META", GDK_META_MASK
}, // meta (since 2.10)
113 /* construct a hash from the var_name_to_ptr array for quick access */
115 make_var_to_name_hash() {
116 uzbl
.comm
.proto_var
= g_hash_table_new(g_str_hash
, g_str_equal
);
118 g_hash_table_insert(uzbl
.comm
.proto_var
, n2v_p
->name
, n2v_p
->ptr
);
123 /* commandline arguments (set initial values for the state variables) */
124 static GOptionEntry entries
[] =
126 { "uri", 'u', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.uri
, "Uri to load", "URI" },
127 { "name", 'n', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.instance_name
, "Name of the current instance (defaults to Xorg window id)", "NAME" },
128 { "config", 'c', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.config_file
, "Config file", "FILE" },
129 { NULL
, 0, 0, 0, NULL
, NULL
, NULL
}
132 typedef void (*Command
)(WebKitWebView
*, const char *);
135 static char *XDG_CONFIG_HOME_default
[256];
136 static char *XDG_CONFIG_DIRS_default
= "/etc/xdg";
139 /* --- UTILITY FUNCTIONS --- */
145 snprintf(tmp
, sizeof(tmp
), "%i", val
);
146 return g_strdup(tmp
);
150 setup_signal(int signr
, sigfunc
*shandler
) {
151 struct sigaction nh
, oh
;
153 nh
.sa_handler
= shandler
;
154 sigemptyset(&nh
.sa_mask
);
157 if(sigaction(signr
, &nh
, &oh
) < 0)
165 if (uzbl
.behave
.fifo_dir
)
166 unlink (uzbl
.comm
.fifo_path
);
167 if (uzbl
.behave
.socket_dir
)
168 unlink (uzbl
.comm
.socket_path
);
170 g_string_free(uzbl
.state
.keycmd
, TRUE
);
171 g_hash_table_destroy(uzbl
.bindings
);
172 g_hash_table_destroy(uzbl
.behave
.commands
);
176 /* --- SIGNAL HANDLER --- */
179 catch_sigterm(int s
) {
185 catch_sigint(int s
) {
191 /* --- CALLBACKS --- */
194 new_window_cb (WebKitWebView
*web_view
, WebKitWebFrame
*frame
, WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*navigation_action
, WebKitWebPolicyDecision
*policy_decision
, gpointer user_data
) {
197 (void) navigation_action
;
198 (void) policy_decision
;
200 const gchar
* uri
= webkit_network_request_get_uri (request
);
201 printf("New window requested -> %s \n", uri
);
202 new_window_load_uri(uri
);
207 create_web_view_cb (WebKitWebView
*web_view
, WebKitWebFrame
*frame
, gpointer user_data
) {
211 if (uzbl
.state
.selected_url
[0]!=0) {
212 printf("\nNew web view -> %s\n",uzbl
.state
.selected_url
);
213 new_window_load_uri(uzbl
.state
.selected_url
);
215 printf("New web view -> %s\n","Nothing to open, exiting");
221 download_cb (WebKitWebView
*web_view
, GObject
*download
, gpointer user_data
) {
224 if (uzbl
.behave
.download_handler
) {
225 const gchar
* uri
= webkit_download_get_uri ((WebKitDownload
*)download
);
226 printf("Download -> %s\n",uri
);
227 run_command(uzbl
.behave
.download_handler
, uri
, FALSE
, NULL
);
232 /* scroll a bar in a given direction */
234 scroll (GtkAdjustment
* bar
, const char *param
) {
238 amount
= g_ascii_strtod(param
, &end
);
241 fprintf(stderr
, "found something after double: %s\n", end
);
243 gtk_adjustment_set_value (bar
, gtk_adjustment_get_value(bar
)+amount
);
246 static void scroll_begin(WebKitWebView
* page
, const char *param
) {
247 (void) page
; (void) param
;
248 gtk_adjustment_set_value (uzbl
.gui
.bar_v
, gtk_adjustment_get_lower(uzbl
.gui
.bar_v
));
251 static void scroll_end(WebKitWebView
* page
, const char *param
) {
252 (void) page
; (void) param
;
253 gtk_adjustment_set_value (uzbl
.gui
.bar_v
, gtk_adjustment_get_upper(uzbl
.gui
.bar_v
) -
254 gtk_adjustment_get_page_size(uzbl
.gui
.bar_v
));
257 static void scroll_vert(WebKitWebView
* page
, const char *param
) {
259 scroll(uzbl
.gui
.bar_v
, param
);
262 static void scroll_horz(WebKitWebView
* page
, const char *param
) {
264 scroll(uzbl
.gui
.bar_h
, param
);
269 if (!uzbl
.behave
.show_status
) {
270 gtk_widget_hide(uzbl
.gui
.mainbar
);
272 gtk_widget_show(uzbl
.gui
.mainbar
);
278 toggle_status_cb (WebKitWebView
* page
, const char *param
) {
282 if (uzbl
.behave
.show_status
) {
283 gtk_widget_hide(uzbl
.gui
.mainbar
);
285 gtk_widget_show(uzbl
.gui
.mainbar
);
287 uzbl
.behave
.show_status
= !uzbl
.behave
.show_status
;
292 link_hover_cb (WebKitWebView
* page
, const gchar
* title
, const gchar
* link
, gpointer data
) {
296 //ADD HOVER URL TO WINDOW TITLE
297 uzbl
.state
.selected_url
[0] = '\0';
299 strcpy (uzbl
.state
.selected_url
, link
);
305 title_change_cb (WebKitWebView
* web_view
, WebKitWebFrame
* web_frame
, const gchar
* title
, gpointer data
) {
309 if (uzbl
.gui
.main_title
)
310 g_free (uzbl
.gui
.main_title
);
311 uzbl
.gui
.main_title
= g_strdup (title
);
316 progress_change_cb (WebKitWebView
* page
, gint progress
, gpointer data
) {
319 uzbl
.gui
.sbar
.load_progress
= progress
;
324 load_finish_cb (WebKitWebView
* page
, WebKitWebFrame
* frame
, gpointer data
) {
328 if (uzbl
.behave
.load_finish_handler
) {
329 run_command(uzbl
.behave
.load_finish_handler
, NULL
, FALSE
, NULL
);
334 load_commit_cb (WebKitWebView
* page
, WebKitWebFrame
* frame
, gpointer data
) {
337 free (uzbl
.state
.uri
);
338 GString
* newuri
= g_string_new (webkit_web_frame_get_uri (frame
));
339 uzbl
.state
.uri
= g_string_free (newuri
, FALSE
);
340 if (uzbl
.behave
.reset_command_mode
&& uzbl
.behave
.insert_mode
) {
341 uzbl
.behave
.insert_mode
= uzbl
.behave
.always_insert_mode
;
344 g_string_truncate(uzbl
.state
.keycmd
, 0); // don't need old commands to remain on new page?
348 destroy_cb (GtkWidget
* widget
, gpointer data
) {
356 if (uzbl
.behave
.history_handler
) {
358 struct tm
* timeinfo
;
361 timeinfo
= localtime ( &rawtime
);
362 strftime (date
, 80, "%Y-%m-%d %H:%M:%S", timeinfo
);
363 GString
* args
= g_string_new ("");
364 g_string_printf (args
, "'%s'", date
);
365 run_command(uzbl
.behave
.history_handler
, args
->str
, FALSE
, NULL
);
366 g_string_free (args
, TRUE
);
371 /* VIEW funcs (little webkit wrappers) */
372 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
374 VIEWFUNC(reload_bypass_cache
)
375 VIEWFUNC(stop_loading
)
382 /* -- command to callback/function map for things we cannot attach to any signals */
385 static struct {char *name
; Command command
;} cmdlist
[] =
387 { "back", view_go_back
},
388 { "forward", view_go_forward
},
389 { "scroll_vert", scroll_vert
},
390 { "scroll_horz", scroll_horz
},
391 { "scroll_begin", scroll_begin
},
392 { "scroll_end", scroll_end
},
393 { "reload", view_reload
, },
394 { "reload_ign_cache", view_reload_bypass_cache
},
395 { "stop", view_stop_loading
, },
396 { "zoom_in", view_zoom_in
, }, //Can crash (when max zoom reached?).
397 { "zoom_out", view_zoom_out
, },
399 { "script", run_js
},
400 { "toggle_status", toggle_status_cb
},
402 { "exit", close_uzbl
},
403 { "search", search_text
},
404 { "insert_mode", set_insert_mode
},
412 uzbl
.behave
.commands
= g_hash_table_new(g_str_hash
, g_str_equal
);
414 for (i
= 0; i
< LENGTH(cmdlist
); i
++)
415 g_hash_table_insert(uzbl
.behave
.commands
, cmdlist
[i
].name
, cmdlist
[i
].command
);
418 /* -- CORE FUNCTIONS -- */
421 free_action(gpointer act
) {
422 Action
*action
= (Action
*)act
;
423 g_free(action
->name
);
425 g_free(action
->param
);
430 new_action(const gchar
*name
, const gchar
*param
) {
431 Action
*action
= g_new(Action
, 1);
433 action
->name
= g_strdup(name
);
435 action
->param
= g_strdup(param
);
437 action
->param
= NULL
;
443 file_exists (const char * filename
) {
444 return (access(filename
, F_OK
) == 0);
448 set_insert_mode(WebKitWebView
*page
, const gchar
*param
) {
452 uzbl
.behave
.insert_mode
= TRUE
;
457 load_uri (WebKitWebView
* web_view
, const gchar
*param
) {
459 GString
* newuri
= g_string_new (param
);
460 if (g_strrstr (param
, "://") == NULL
)
461 g_string_prepend (newuri
, "http://");
462 /* if we do handle cookies, ask our handler for them */
463 webkit_web_view_load_uri (web_view
, newuri
->str
);
464 g_string_free (newuri
, TRUE
);
469 run_js (WebKitWebView
* web_view
, const gchar
*param
) {
471 webkit_web_view_execute_script (web_view
, param
);
475 search_text (WebKitWebView
*page
, const char *param
) {
476 if ((param
) && (param
[0] != '\0')) {
477 strcpy(uzbl
.state
.searchtx
, param
);
479 if (uzbl
.state
.searchtx
[0] != '\0') {
480 printf ("Searching: %s\n", uzbl
.state
.searchtx
);
481 webkit_web_view_unmark_text_matches (page
);
482 webkit_web_view_mark_text_matches (page
, uzbl
.state
.searchtx
, FALSE
, 0);
483 webkit_web_view_set_highlight_text_matches (page
, TRUE
);
484 webkit_web_view_search_text (page
, uzbl
.state
.searchtx
, FALSE
, TRUE
, TRUE
);
489 new_window_load_uri (const gchar
* uri
) {
490 GString
* to_execute
= g_string_new ("");
491 g_string_append_printf (to_execute
, "%s --uri '%s'", uzbl
.state
.executable_path
, uri
);
493 for (i
= 0; entries
[i
].long_name
!= NULL
; i
++) {
494 if ((entries
[i
].arg
== G_OPTION_ARG_STRING
) && (strcmp(entries
[i
].long_name
,"uri")!=0)) {
495 gchar
** str
= (gchar
**)entries
[i
].arg_data
;
497 g_string_append_printf (to_execute
, " --%s '%s'", entries
[i
].long_name
, *str
);
501 printf("\n%s\n", to_execute
->str
);
502 g_spawn_command_line_async (to_execute
->str
, NULL
);
503 g_string_free (to_execute
, TRUE
);
507 close_uzbl (WebKitWebView
*page
, const char *param
) {
513 /* --Statusbar functions-- */
515 build_progressbar_ascii(int percent
) {
519 GString
*bar
= g_string_new("");
521 l
= (double)percent
*((double)width
/100.);
522 l
= (int)(l
+.5)>=(int)l
? l
+.5 : l
;
524 for(i
=0; i
<(int)l
; i
++)
525 g_string_append(bar
, "=");
528 g_string_append(bar
, "·");
530 return g_string_free(bar
, FALSE
);
535 const GScannerConfig scan_config
= {
538 ) /* cset_skip_characters */,
543 ) /* cset_identifier_first */,
550 ) /* cset_identifier_nth */,
551 ( "" ) /* cpair_comment_single */,
553 TRUE
/* case_sensitive */,
555 FALSE
/* skip_comment_multi */,
556 FALSE
/* skip_comment_single */,
557 FALSE
/* scan_comment_multi */,
558 TRUE
/* scan_identifier */,
559 TRUE
/* scan_identifier_1char */,
560 FALSE
/* scan_identifier_NULL */,
561 TRUE
/* scan_symbols */,
562 FALSE
/* scan_binary */,
563 FALSE
/* scan_octal */,
564 FALSE
/* scan_float */,
565 FALSE
/* scan_hex */,
566 FALSE
/* scan_hex_dollar */,
567 FALSE
/* scan_string_sq */,
568 FALSE
/* scan_string_dq */,
569 TRUE
/* numbers_2_int */,
570 FALSE
/* int_2_float */,
571 FALSE
/* identifier_2_string */,
572 FALSE
/* char_2_token */,
573 FALSE
/* symbol_2_token */,
574 TRUE
/* scope_0_fallback */,
579 uzbl
.scan
= g_scanner_new(&scan_config
);
580 while(symp
->symbol_name
) {
581 g_scanner_scope_add_symbol(uzbl
.scan
, 0,
583 GINT_TO_POINTER(symp
->symbol_token
));
589 expand_template(const char *template) {
590 if(!template) return NULL
;
592 GTokenType token
= G_TOKEN_NONE
;
593 GString
*ret
= g_string_new("");
597 g_scanner_input_text(uzbl
.scan
, template, strlen(template));
598 while(!g_scanner_eof(uzbl
.scan
) && token
!= G_TOKEN_LAST
) {
599 token
= g_scanner_get_next_token(uzbl
.scan
);
601 if(token
== G_TOKEN_SYMBOL
) {
602 sym
= (int)g_scanner_cur_value(uzbl
.scan
).v_symbol
;
607 g_markup_printf_escaped("%s", uzbl
.state
.uri
):"");
610 buf
= itos(uzbl
.gui
.sbar
.load_progress
);
611 g_string_append(ret
, buf
);
614 case SYM_LOADPRGSBAR
:
615 buf
= build_progressbar_ascii(uzbl
.gui
.sbar
.load_progress
);
616 g_string_append(ret
, buf
);
622 g_markup_printf_escaped("%s", uzbl
.gui
.main_title
):"");
625 buf
= itos(uzbl
.xwin
);
627 uzbl
.state
.instance_name
?uzbl
.state
.instance_name
:buf
);
632 uzbl
.state
.keycmd
->str
?
633 g_markup_printf_escaped("%s", uzbl
.state
.keycmd
->str
):"");
637 uzbl
.behave
.insert_mode
?"[I]":"[C]");
641 uzbl
.gui
.sbar
.msg
?uzbl
.gui
.sbar
.msg
:"");
645 buf
= itos(WEBKIT_MAJOR_VERSION
);
646 g_string_append(ret
, buf
);
650 buf
= itos(WEBKIT_MINOR_VERSION
);
651 g_string_append(ret
, buf
);
655 buf
= itos(WEBKIT_MICRO_VERSION
);
656 g_string_append(ret
, buf
);
660 g_string_append(ret
, uzbl
.state
.unameinfo
.sysname
);
663 g_string_append(ret
, uzbl
.state
.unameinfo
.nodename
);
666 g_string_append(ret
, uzbl
.state
.unameinfo
.release
);
669 g_string_append(ret
, uzbl
.state
.unameinfo
.version
);
672 g_string_append(ret
, uzbl
.state
.unameinfo
.machine
);
675 g_string_append(ret
, ARCH
);
679 g_string_append(ret
, uzbl
.state
.unameinfo
.domainname
);
683 g_string_append(ret
, COMMIT
);
689 else if(token
== G_TOKEN_INT
) {
690 buf
= itos(g_scanner_cur_value(uzbl
.scan
).v_int
);
691 g_string_append(ret
, buf
);
694 else if(token
== G_TOKEN_IDENTIFIER
) {
695 g_string_append(ret
, (gchar
*)g_scanner_cur_value(uzbl
.scan
).v_identifier
);
697 else if(token
== G_TOKEN_CHAR
) {
698 g_string_append_c(ret
, (gchar
)g_scanner_cur_value(uzbl
.scan
).v_char
);
702 return g_string_free(ret
, FALSE
);
704 /* --End Statusbar functions-- */
707 // make sure to put '' around args, so that if there is whitespace we can still keep arguments together.
709 run_command (const char *command
, const char *args
, const gboolean sync
, char **stdout
) {
710 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
711 GString
* to_execute
= g_string_new ("");
713 g_string_printf (to_execute
, "%s '%s' '%i' '%i' '%s' '%s'",
714 command
, (uzbl
.state
.config_file
? uzbl
.state
.config_file
: "(null)"),
715 (int) getpid(), (int) uzbl
.xwin
, uzbl
.comm
.fifo_path
,
716 uzbl
.comm
.socket_path
);
717 g_string_append_printf (to_execute
, " '%s' '%s'",
718 uzbl
.state
.uri
, uzbl
.gui
.main_title
);
719 if(args
) g_string_append_printf (to_execute
, " %s", args
);
722 result
= g_spawn_command_line_sync (to_execute
->str
, stdout
, NULL
, NULL
, NULL
);
723 } else result
= g_spawn_command_line_async (to_execute
->str
, NULL
);
724 printf("Called %s. Result: %s\n", to_execute
->str
, (result
? "TRUE" : "FALSE" ));
725 g_string_free (to_execute
, TRUE
);
730 spawn(WebKitWebView
*web_view
, const char *param
) {
732 run_command(param
, NULL
, FALSE
, NULL
);
736 parse_command(const char *cmd
, const char *param
) {
739 if ((c
= g_hash_table_lookup(uzbl
.behave
.commands
, cmd
)))
740 c(uzbl
.gui
.web_view
, param
);
742 fprintf (stderr
, "command \"%s\" not understood. ignoring.\n", cmd
);
750 uzbl
.comm
.get_regex
= g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
751 G_REGEX_OPTIMIZE
, 0, &err
);
752 uzbl
.comm
.set_regex
= g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
753 G_REGEX_OPTIMIZE
, 0, &err
);
754 uzbl
.comm
.bind_regex
= g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
755 G_REGEX_UNGREEDY
|G_REGEX_OPTIMIZE
, 0, &err
);
756 uzbl
.comm
.act_regex
= g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
757 G_REGEX_OPTIMIZE
, 0, &err
);
758 uzbl
.comm
.keycmd_regex
= g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
759 G_REGEX_OPTIMIZE
, 0, &err
);
763 get_var_value(gchar
*name
) {
766 if( (p
= g_hash_table_lookup(uzbl
.comm
.proto_var
, name
)) ) {
767 if(var_is("status_format", name
)
768 || var_is("useragent", name
)) {
769 printf("VAR: %s VALUE: %s\n", name
, (char *)*p
);
770 } else printf("VAR: %s VALUE: %d\n", name
, (int)*p
);
779 if(*uzbl
.net
.proxy_url
== ' '
780 || uzbl
.net
.proxy_url
== NULL
) {
781 soup_session_remove_feature_by_type(uzbl
.net
.soup_session
,
782 (GType
) SOUP_SESSION_PROXY_URI
);
785 suri
= soup_uri_new(uzbl
.net
.proxy_url
);
786 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
787 SOUP_SESSION_PROXY_URI
,
797 gtk_widget_ref(uzbl
.gui
.scrolled_win
);
798 gtk_widget_ref(uzbl
.gui
.mainbar
);
799 gtk_container_remove(GTK_CONTAINER(uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
);
800 gtk_container_remove(GTK_CONTAINER(uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
);
802 if(uzbl
.behave
.status_top
) {
803 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
804 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
807 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
808 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
810 gtk_widget_unref(uzbl
.gui
.scrolled_win
);
811 gtk_widget_unref(uzbl
.gui
.mainbar
);
812 gtk_widget_grab_focus (GTK_WIDGET (uzbl
.gui
.web_view
));
816 var_is(const char *x
, const char *y
) {
817 return (strcmp(x
, y
) == 0 ? TRUE
: FALSE
);
821 set_var_value(gchar
*name
, gchar
*val
) {
825 if( (p
= g_hash_table_lookup(uzbl
.comm
.proto_var
, name
)) ) {
826 if(var_is("status_message", name
)
827 || var_is("status_background", name
)
828 || var_is("status_format", name
)
829 || var_is("load_finish_handler", name
)
830 || var_is("history_handler", name
)
831 || var_is("download_handler", name
)
832 || var_is("cookie_handler", name
)) {
838 else if(var_is("uri", name
)) {
841 load_uri(uzbl
.gui
.web_view
, (const gchar
*)*p
);
843 else if(var_is("proxy_url", name
)) {
848 else if(var_is("fifo_dir", name
)) {
850 *p
= init_fifo(g_strdup(val
));
852 else if(var_is("socket_dir", name
)) {
854 *p
= init_socket(g_strdup(val
));
856 else if(var_is("modkey", name
)) {
859 *p
= g_utf8_strup(val
, -1);
860 uzbl
.behave
.modmask
= 0;
861 for (i
= 0; modkeys
[i
].key
!= NULL
; i
++) {
862 if (g_strrstr(*p
, modkeys
[i
].key
))
863 uzbl
.behave
.modmask
|= modkeys
[i
].mask
;
866 else if(var_is("useragent", name
)) {
868 *p
= set_useragent(g_strdup(val
));
870 /* variables that take int values */
873 *ip
= (int)strtoul(val
, &endp
, 10);
875 if(var_is("show_status", name
)) {
878 else if(var_is("always_insert_mode", name
)) {
879 uzbl
.behave
.insert_mode
=
880 uzbl
.behave
.always_insert_mode
? TRUE
: FALSE
;
883 else if (var_is("max_conns", name
)) {
884 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
885 SOUP_SESSION_MAX_CONNS
, uzbl
.net
.max_conns
, NULL
);
887 else if (var_is("max_conns_host", name
)) {
888 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
889 SOUP_SESSION_MAX_CONNS_PER_HOST
, uzbl
.net
.max_conns_host
, NULL
);
891 else if (var_is("http_debug", name
)) {
892 //soup_session_remove_feature
893 // (uzbl.net.soup_session, uzbl.net.soup_logger);
894 soup_session_remove_feature
895 (uzbl
.net
.soup_session
, SOUP_SESSION_FEATURE(uzbl
.net
.soup_logger
));
896 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
897 /*g_free(uzbl.net.soup_logger);*/
899 uzbl
.net
.soup_logger
= soup_logger_new(uzbl
.behave
.http_debug
, -1);
900 soup_session_add_feature(uzbl
.net
.soup_session
,
901 SOUP_SESSION_FEATURE(uzbl
.net
.soup_logger
));
903 else if (var_is("status_top", name
)) {
912 runcmd(WebKitWebView
* page
, const char *param
) {
914 parse_cmd_line(param
);
918 parse_cmd_line(const char *ctl_line
) {
922 if(ctl_line
[0] == 's' || ctl_line
[0] == 'S') {
923 tokens
= g_regex_split(uzbl
.comm
.set_regex
, ctl_line
, 0);
924 if(tokens
[0][0] == 0) {
925 set_var_value(tokens
[1], tokens
[2]);
929 printf("Error in command: %s\n", tokens
[0]);
932 else if(ctl_line
[0] == 'g' || ctl_line
[0] == 'G') {
933 tokens
= g_regex_split(uzbl
.comm
.get_regex
, ctl_line
, 0);
934 if(tokens
[0][0] == 0) {
935 get_var_value(tokens
[1]);
939 printf("Error in command: %s\n", tokens
[0]);
942 else if(ctl_line
[0] == 'b' || ctl_line
[0] == 'B') {
943 tokens
= g_regex_split(uzbl
.comm
.bind_regex
, ctl_line
, 0);
944 if(tokens
[0][0] == 0) {
945 add_binding(tokens
[1], tokens
[2]);
949 printf("Error in command: %s\n", tokens
[0]);
952 else if(ctl_line
[0] == 'A' || ctl_line
[0] == 'a') {
953 tokens
= g_regex_split(uzbl
.comm
.act_regex
, ctl_line
, 0);
954 if(tokens
[0][0] == 0) {
955 parse_command(tokens
[1], tokens
[2]);
959 printf("Error in command: %s\n", tokens
[0]);
962 else if(ctl_line
[0] == 'K' || ctl_line
[0] == 'k') {
963 tokens
= g_regex_split(uzbl
.comm
.keycmd_regex
, ctl_line
, 0);
964 if(tokens
[0][0] == 0) {
965 /* should incremental commands want each individual "keystroke"
966 sent in a loop or the whole string in one go like now? */
967 g_string_assign(uzbl
.state
.keycmd
, tokens
[1]);
974 else if( (ctl_line
[0] == '#')
975 || (ctl_line
[0] == ' ')
976 || (ctl_line
[0] == '\n'))
977 ; /* ignore these lines */
979 printf("Command not understood (%s)\n", ctl_line
);
985 build_stream_name(int type
, const gchar
* dir
) {
987 State
*s
= &uzbl
.state
;
990 xwin_str
= itos((int)uzbl
.xwin
);
992 str
= g_strdup_printf
993 ("%s/uzbl_fifo_%s", dir
,
994 s
->instance_name
? s
->instance_name
: xwin_str
);
995 } else if (type
== SOCKET
) {
996 str
= g_strdup_printf
997 ("%s/uzbl_socket_%s", dir
,
998 s
->instance_name
? s
->instance_name
: xwin_str
);
1005 control_fifo(GIOChannel
*gio
, GIOCondition condition
) {
1006 printf("triggered\n");
1011 if (condition
& G_IO_HUP
)
1012 g_error ("Fifo: Read end of pipe died!\n");
1015 g_error ("Fifo: GIOChannel broke\n");
1017 ret
= g_io_channel_read_line(gio
, &ctl_line
, NULL
, NULL
, &err
);
1018 if (ret
== G_IO_STATUS_ERROR
)
1019 g_error ("Fifo: Error reading: %s\n", err
->message
);
1021 parse_cmd_line(ctl_line
);
1028 init_fifo(gchar
*dir
) { /* return dir or, on error, free dir and return NULL */
1029 if (uzbl
.comm
.fifo_path
) { /* get rid of the old fifo if one exists */
1030 if (unlink(uzbl
.comm
.fifo_path
) == -1)
1031 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl
.comm
.fifo_path
);
1032 g_free(uzbl
.comm
.fifo_path
);
1033 uzbl
.comm
.fifo_path
= NULL
;
1036 if (*dir
== ' ') { /* space unsets the variable */
1041 GIOChannel
*chan
= NULL
;
1042 GError
*error
= NULL
;
1043 gchar
*path
= build_stream_name(FIFO
, dir
);
1045 if (!file_exists(path
)) {
1046 if (mkfifo (path
, 0666) == 0) {
1047 // 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.
1048 chan
= g_io_channel_new_file(path
, "r+", &error
);
1050 if (g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_fifo
, NULL
)) {
1051 printf ("init_fifo: created successfully as %s\n", path
);
1052 uzbl
.comm
.fifo_path
= path
;
1054 } else g_warning ("init_fifo: could not add watch on %s\n", path
);
1055 } else g_warning ("init_fifo: can't open: %s\n", error
->message
);
1056 } else g_warning ("init_fifo: can't create %s: %s\n", path
, strerror(errno
));
1057 } else g_warning ("init_fifo: can't create %s: file exists\n", path
);
1059 /* if we got this far, there was an error; cleanup */
1066 control_stdin(GIOChannel
*gio
, GIOCondition condition
) {
1067 gchar
*ctl_line
= NULL
;
1068 gsize ctl_line_len
= 0;
1072 if (condition
& G_IO_HUP
) {
1073 ret
= g_io_channel_shutdown (gio
, FALSE
, &err
);
1077 ret
= g_io_channel_read_line(gio
, &ctl_line
, &ctl_line_len
, NULL
, &err
);
1078 if ( (ret
== G_IO_STATUS_ERROR
) || (ret
== G_IO_STATUS_EOF
) )
1081 parse_cmd_line(ctl_line
);
1089 GIOChannel
*chan
= NULL
;
1090 GError
*error
= NULL
;
1092 chan
= g_io_channel_unix_new(fileno(stdin
));
1094 if (!g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_stdin
, NULL
)) {
1095 g_error ("Stdin: could not add watch\n");
1097 printf ("Stdin: watch added successfully\n");
1100 g_error ("Stdin: Error while opening: %s\n", error
->message
);
1105 control_socket(GIOChannel
*chan
) {
1106 struct sockaddr_un remote
;
1107 char buffer
[512], *ctl_line
;
1109 int sock
, clientsock
, n
, done
;
1112 sock
= g_io_channel_unix_get_fd(chan
);
1114 memset (buffer
, 0, sizeof (buffer
));
1116 t
= sizeof (remote
);
1117 clientsock
= accept (sock
, (struct sockaddr
*) &remote
, &t
);
1121 memset (temp
, 0, sizeof (temp
));
1122 n
= recv (clientsock
, temp
, 128, 0);
1124 buffer
[strlen (buffer
)] = '\0';
1128 strcat (buffer
, temp
);
1131 if (strcmp (buffer
, "\n") < 0) {
1132 buffer
[strlen (buffer
) - 1] = '\0';
1134 buffer
[strlen (buffer
)] = '\0';
1137 ctl_line
= g_strdup(buffer
);
1138 parse_cmd_line (ctl_line
);
1141 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1142 GError *error = NULL;
1145 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1146 if (ret == G_IO_STATUS_ERROR)
1147 g_error ("Error reading: %s\n", error->message);
1149 printf("Got line %s (%u bytes) \n",ctl_line, len);
1151 parse_line(ctl_line);
1159 init_socket(gchar
*dir
) { /* return dir or, on error, free dir and return NULL */
1160 if (uzbl
.comm
.socket_path
) { /* remove an existing socket should one exist */
1161 if (unlink(uzbl
.comm
.socket_path
) == -1)
1162 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl
.comm
.socket_path
);
1163 g_free(uzbl
.comm
.socket_path
);
1164 uzbl
.comm
.socket_path
= NULL
;
1172 GIOChannel
*chan
= NULL
;
1174 struct sockaddr_un local
;
1175 gchar
*path
= build_stream_name(SOCKET
, dir
);
1177 sock
= socket (AF_UNIX
, SOCK_STREAM
, 0);
1179 local
.sun_family
= AF_UNIX
;
1180 strcpy (local
.sun_path
, path
);
1181 unlink (local
.sun_path
);
1183 len
= strlen (local
.sun_path
) + sizeof (local
.sun_family
);
1184 if (bind (sock
, (struct sockaddr
*) &local
, len
) != -1) {
1185 printf ("init_socket: opened in %s\n", path
);
1188 if( (chan
= g_io_channel_unix_new(sock
)) ) {
1189 g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_socket
, chan
);
1190 uzbl
.comm
.socket_path
= path
;
1193 } else g_warning ("init_socket: could not open in %s: %s\n", path
, strerror(errno
));
1195 /* if we got this far, there was an error; cleanup */
1202 update_title (void) {
1203 GString
* string_long
= g_string_new ("");
1204 GString
* string_short
= g_string_new ("");
1208 State
*s
= &uzbl
.state
;
1209 Behaviour
*b
= &uzbl
.behave
;
1211 if(s
->instance_name
) {
1212 iname_len
= strlen(s
->instance_name
)+4;
1213 iname
= malloc(iname_len
);
1214 snprintf(iname
, iname_len
, "<%s> ", s
->instance_name
);
1216 g_string_prepend(string_long
, iname
);
1217 g_string_prepend(string_short
, iname
);
1221 g_string_append_printf(string_long
, "%s ", s
->keycmd
->str
);
1222 if (!b
->always_insert_mode
)
1223 g_string_append (string_long
, (b
->insert_mode
? "[I] " : "[C] "));
1224 if (uzbl
.gui
.main_title
) {
1225 g_string_append (string_long
, uzbl
.gui
.main_title
);
1226 g_string_append (string_short
, uzbl
.gui
.main_title
);
1228 g_string_append (string_long
, " - Uzbl browser");
1229 g_string_append (string_short
, " - Uzbl browser");
1230 if (s
->selected_url
[0]!=0) {
1231 g_string_append_printf (string_long
, " -> (%s)", s
->selected_url
);
1234 gchar
* title_long
= g_string_free (string_long
, FALSE
);
1235 gchar
* title_short
= g_string_free (string_short
, FALSE
);
1237 if (b
->show_status
) {
1238 gtk_window_set_title (GTK_WINDOW(uzbl
.gui
.main_window
), title_short
);
1239 // TODO: we should probably not do this every time we want to update the title..?
1240 statln
= expand_template(uzbl
.behave
.status_format
);
1241 gtk_label_set_markup(GTK_LABEL(uzbl
.gui
.mainbar_label
), statln
);
1242 if (b
->status_background
) {
1244 gdk_color_parse (b
->status_background
, &color
);
1245 //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)
1246 gtk_widget_modify_bg (uzbl
.gui
.main_window
, GTK_STATE_NORMAL
, &color
);
1250 gtk_window_set_title (GTK_WINDOW(uzbl
.gui
.main_window
), title_long
);
1253 g_free (title_long
);
1254 g_free (title_short
);
1258 key_press_cb (WebKitWebView
* page
, GdkEventKey
* event
)
1260 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1264 if (event
->type
!= GDK_KEY_PRESS
|| event
->keyval
== GDK_Page_Up
|| event
->keyval
== GDK_Page_Down
1265 || 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
)
1268 /* turn off insert mode (if always_insert_mode is not used) */
1269 if (uzbl
.behave
.insert_mode
&& (event
->keyval
== GDK_Escape
)) {
1270 uzbl
.behave
.insert_mode
= uzbl
.behave
.always_insert_mode
;
1275 if (uzbl
.behave
.insert_mode
&& (((event
->state
& uzbl
.behave
.modmask
) != uzbl
.behave
.modmask
) || (!uzbl
.behave
.modmask
)))
1278 if (event
->keyval
== GDK_Escape
) {
1279 g_string_truncate(uzbl
.state
.keycmd
, 0);
1284 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1285 if (event
->keyval
== GDK_Insert
) {
1287 if ((event
->state
& GDK_SHIFT_MASK
) == GDK_SHIFT_MASK
) {
1288 str
= gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY
));
1290 str
= gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
));
1293 g_string_append (uzbl
.state
.keycmd
, str
);
1300 if ((event
->keyval
== GDK_BackSpace
) && (uzbl
.state
.keycmd
->len
> 0)) {
1301 g_string_truncate(uzbl
.state
.keycmd
, uzbl
.state
.keycmd
->len
- 1);
1305 gboolean key_ret
= FALSE
;
1306 if ((event
->keyval
== GDK_Return
) || (event
->keyval
== GDK_KP_Enter
))
1308 if (!key_ret
) g_string_append(uzbl
.state
.keycmd
, event
->string
);
1310 run_keycmd(key_ret
);
1312 if (key_ret
) return (!uzbl
.behave
.insert_mode
);
1317 run_keycmd(const gboolean key_ret
) {
1318 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1320 if ((action
= g_hash_table_lookup(uzbl
.bindings
, uzbl
.state
.keycmd
->str
))) {
1321 g_string_truncate(uzbl
.state
.keycmd
, 0);
1322 parse_command(action
->name
, action
->param
);
1326 /* try if it's an incremental keycmd or one that takes args, and run it */
1327 GString
* short_keys
= g_string_new ("");
1328 GString
* short_keys_inc
= g_string_new ("");
1330 for (i
=0; i
<(uzbl
.state
.keycmd
->len
); i
++) {
1331 g_string_append_c(short_keys
, uzbl
.state
.keycmd
->str
[i
]);
1332 g_string_assign(short_keys_inc
, short_keys
->str
);
1333 g_string_append_c(short_keys
, '_');
1334 g_string_append_c(short_keys_inc
, '*');
1336 gboolean exec_now
= FALSE
;
1337 if ((action
= g_hash_table_lookup(uzbl
.bindings
, short_keys
->str
))) {
1338 if (key_ret
) exec_now
= TRUE
; /* run normal cmds only if return was pressed */
1339 } else if ((action
= g_hash_table_lookup(uzbl
.bindings
, short_keys_inc
->str
))) {
1340 if (key_ret
) { /* just quit the incremental command on return */
1341 g_string_truncate(uzbl
.state
.keycmd
, 0);
1343 } else exec_now
= TRUE
; /* always exec incr. commands on keys other than return */
1347 GString
* parampart
= g_string_new (uzbl
.state
.keycmd
->str
);
1348 GString
* actionname
= g_string_new ("");
1349 GString
* actionparam
= g_string_new ("");
1350 g_string_erase (parampart
, 0, i
+1);
1352 g_string_printf (actionname
, action
->name
, parampart
->str
);
1354 g_string_printf (actionparam
, action
->param
, parampart
->str
);
1355 parse_command(actionname
->str
, actionparam
->str
);
1356 g_string_free (actionname
, TRUE
);
1357 g_string_free (actionparam
, TRUE
);
1358 g_string_free (parampart
, TRUE
);
1360 g_string_truncate(uzbl
.state
.keycmd
, 0);
1364 g_string_truncate(short_keys
, short_keys
->len
- 1);
1366 g_string_free (short_keys
, TRUE
);
1367 g_string_free (short_keys_inc
, TRUE
);
1374 GtkWidget
* scrolled_window
= gtk_scrolled_window_new (NULL
, NULL
);
1375 //main_window_ref = g_object_ref(scrolled_window);
1376 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
1378 g
->web_view
= WEBKIT_WEB_VIEW (webkit_web_view_new ());
1379 gtk_container_add (GTK_CONTAINER (scrolled_window
), GTK_WIDGET (g
->web_view
));
1381 g_signal_connect (G_OBJECT (g
->web_view
), "title-changed", G_CALLBACK (title_change_cb
), g
->web_view
);
1382 g_signal_connect (G_OBJECT (g
->web_view
), "load-progress-changed", G_CALLBACK (progress_change_cb
), g
->web_view
);
1383 g_signal_connect (G_OBJECT (g
->web_view
), "load-committed", G_CALLBACK (load_commit_cb
), g
->web_view
);
1384 g_signal_connect (G_OBJECT (g
->web_view
), "load-finished", G_CALLBACK (log_history_cb
), g
->web_view
);
1385 g_signal_connect (G_OBJECT (g
->web_view
), "load-finished", G_CALLBACK (load_finish_cb
), g
->web_view
);
1386 g_signal_connect (G_OBJECT (g
->web_view
), "hovering-over-link", G_CALLBACK (link_hover_cb
), g
->web_view
);
1387 g_signal_connect (G_OBJECT (g
->web_view
), "key-press-event", G_CALLBACK (key_press_cb
), g
->web_view
);
1388 g_signal_connect (G_OBJECT (g
->web_view
), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb
), g
->web_view
);
1389 g_signal_connect (G_OBJECT (g
->web_view
), "download-requested", G_CALLBACK (download_cb
), g
->web_view
);
1390 g_signal_connect (G_OBJECT (g
->web_view
), "create-web-view", G_CALLBACK (create_web_view_cb
), g
->web_view
);
1392 return scrolled_window
;
1399 g
->mainbar
= gtk_hbox_new (FALSE
, 0);
1401 /* keep a reference to the bar so we can re-pack it at runtime*/
1402 //sbar_ref = g_object_ref(g->mainbar);
1404 g
->mainbar_label
= gtk_label_new ("");
1405 gtk_label_set_selectable((GtkLabel
*)g
->mainbar_label
, TRUE
);
1406 gtk_label_set_ellipsize(GTK_LABEL(g
->mainbar_label
), PANGO_ELLIPSIZE_END
);
1407 gtk_misc_set_alignment (GTK_MISC(g
->mainbar_label
), 0, 0);
1408 gtk_misc_set_padding (GTK_MISC(g
->mainbar_label
), 2, 2);
1409 gtk_box_pack_start (GTK_BOX (g
->mainbar
), g
->mainbar_label
, TRUE
, TRUE
, 0);
1414 GtkWidget
* create_window () {
1415 GtkWidget
* window
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
1416 gtk_window_set_default_size (GTK_WINDOW (window
), 800, 600);
1417 gtk_widget_set_name (window
, "Uzbl browser");
1418 g_signal_connect (G_OBJECT (window
), "destroy", G_CALLBACK (destroy_cb
), NULL
);
1424 add_binding (const gchar
*key
, const gchar
*act
) {
1425 char **parts
= g_strsplit(act
, " ", 2);
1432 printf ("Binding %-10s : %s\n", key
, act
);
1433 action
= new_action(parts
[0], parts
[1]);
1435 if(g_hash_table_lookup(uzbl
.bindings
, key
))
1436 g_hash_table_remove(uzbl
.bindings
, key
);
1437 g_hash_table_insert(uzbl
.bindings
, g_strdup(key
), action
);
1445 State
*s
= &uzbl
.state
;
1446 Network
*n
= &uzbl
.net
;
1448 uzbl
.behave
.reset_command_mode
= 1;
1450 if (!s
->config_file
) {
1451 const char* XDG_CONFIG_HOME
= getenv ("XDG_CONFIG_HOME");
1452 if (! XDG_CONFIG_HOME
|| ! strcmp (XDG_CONFIG_HOME
, "")) {
1453 XDG_CONFIG_HOME
= (char*)XDG_CONFIG_HOME_default
;
1455 printf("XDG_CONFIG_HOME: %s\n", XDG_CONFIG_HOME
);
1457 strcpy (s
->config_file_path
, XDG_CONFIG_HOME
);
1458 strcat (s
->config_file_path
, "/uzbl/config");
1459 if (file_exists (s
->config_file_path
)) {
1460 printf ("Config file %s found.\n", s
->config_file_path
);
1461 s
->config_file
= &s
->config_file_path
[0];
1463 // Now we check $XDG_CONFIG_DIRS
1464 char *XDG_CONFIG_DIRS
= getenv ("XDG_CONFIG_DIRS");
1465 if (! XDG_CONFIG_DIRS
|| ! strcmp (XDG_CONFIG_DIRS
, ""))
1466 XDG_CONFIG_DIRS
= XDG_CONFIG_DIRS_default
;
1468 printf("XDG_CONFIG_DIRS: %s\n", XDG_CONFIG_DIRS
);
1471 strcpy (buffer
, XDG_CONFIG_DIRS
);
1472 const gchar
* dir
= (char *) strtok_r (buffer
, ":", &saveptr
);
1473 while (dir
&& ! file_exists (s
->config_file_path
)) {
1474 strcpy (s
->config_file_path
, dir
);
1475 strcat (s
->config_file_path
, "/uzbl/config_file_pathig");
1476 if (file_exists (s
->config_file_path
)) {
1477 printf ("Config file %s found.\n", s
->config_file_path
);
1478 s
->config_file
= &s
->config_file_path
[0];
1480 dir
= (char * ) strtok_r (NULL
, ":", &saveptr
);
1485 if (s
->config_file
) {
1486 GIOChannel
*chan
= NULL
;
1487 GError
*error
= NULL
;
1488 gchar
*readbuf
= NULL
;
1491 chan
= g_io_channel_new_file(s
->config_file
, "r", &error
);
1494 while (g_io_channel_read_line(chan
, &readbuf
, &len
, NULL
, NULL
)
1495 == G_IO_STATUS_NORMAL
) {
1496 parse_cmd_line(readbuf
);
1500 g_io_channel_unref (chan
);
1501 printf ("Config %s loaded\n", s
->config_file
);
1503 fprintf(stderr
, "uzbl: error loading file%s\n", s
->config_file
);
1506 printf ("No configuration file loaded.\n");
1508 if (!uzbl
.behave
.status_format
)
1509 uzbl
.behave
.status_format
= g_strdup(STATUS_DEFAULT
);
1511 g_signal_connect(n
->soup_session
, "request-queued", G_CALLBACK(handle_cookies
), NULL
);
1515 set_useragent(gchar
*val
) {
1520 gchar
*ua
= expand_template(val
);
1522 g_object_set(G_OBJECT(uzbl
.net
.soup_session
), SOUP_SESSION_USER_AGENT
, ua
, NULL
);
1526 static void handle_cookies (SoupSession
*session
, SoupMessage
*msg
, gpointer user_data
){
1529 if (!uzbl
.behave
.cookie_handler
) return;
1531 gchar
* stdout
= NULL
;
1532 soup_message_add_header_handler(msg
, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies
), NULL
);
1533 GString
* args
= g_string_new ("");
1534 SoupURI
* soup_uri
= soup_message_get_uri(msg
);
1535 g_string_printf (args
, "GET %s %s", soup_uri
->host
, soup_uri
->path
);
1536 run_command(uzbl
.behave
.cookie_handler
, args
->str
, TRUE
, &stdout
);
1538 soup_message_headers_replace (msg
->request_headers
, "Cookie", stdout
);
1540 g_string_free(args
, TRUE
);
1544 save_cookies (SoupMessage
*msg
, gpointer user_data
){
1548 for (ck
= soup_cookies_from_response(msg
); ck
; ck
= ck
->next
){
1549 cookie
= soup_cookie_to_set_cookie_header(ck
->data
);
1550 GString
* args
= g_string_new ("");
1551 SoupURI
* soup_uri
= soup_message_get_uri(msg
);
1552 g_string_printf (args
, "PUT %s %s \"%s\"", soup_uri
->host
, soup_uri
->path
, cookie
);
1553 run_command(uzbl
.behave
.cookie_handler
, args
->str
, FALSE
, NULL
);
1554 g_string_free(args
, TRUE
);
1562 main (int argc
, char* argv
[]) {
1563 gtk_init (&argc
, &argv
);
1564 if (!g_thread_supported ())
1565 g_thread_init (NULL
);
1567 printf("Uzbl start location: %s\n", argv
[0]);
1568 strcpy(uzbl
.state
.executable_path
,argv
[0]);
1570 strcat ((char *) XDG_CONFIG_HOME_default
, getenv ("HOME"));
1571 strcat ((char *) XDG_CONFIG_HOME_default
, "/.config");
1573 GError
*error
= NULL
;
1574 GOptionContext
* context
= g_option_context_new ("- some stuff here maybe someday");
1575 g_option_context_add_main_entries (context
, entries
, NULL
);
1576 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
1577 g_option_context_parse (context
, &argc
, &argv
, &error
);
1578 /* initialize hash table */
1579 uzbl
.bindings
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, free_action
);
1581 uzbl
.net
.soup_session
= webkit_get_default_session();
1582 uzbl
.state
.keycmd
= g_string_new("");
1584 if(setup_signal(SIGTERM
, catch_sigterm
) == SIG_ERR
)
1585 fprintf(stderr
, "uzbl: error hooking SIGTERM\n");
1586 if(setup_signal(SIGINT
, catch_sigint
) == SIG_ERR
)
1587 fprintf(stderr
, "uzbl: error hooking SIGINT\n");
1589 if(uname(&uzbl
.state
.unameinfo
) == -1)
1590 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1595 make_var_to_name_hash();
1598 uzbl
.gui
.vbox
= gtk_vbox_new (FALSE
, 0);
1600 uzbl
.gui
.scrolled_win
= create_browser();
1603 /* initial packing */
1604 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
1605 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
1607 uzbl
.gui
.main_window
= create_window ();
1608 gtk_container_add (GTK_CONTAINER (uzbl
.gui
.main_window
), uzbl
.gui
.vbox
);
1610 load_uri (uzbl
.gui
.web_view
, uzbl
.state
.uri
); //TODO: is this needed?
1612 gtk_widget_grab_focus (GTK_WIDGET (uzbl
.gui
.web_view
));
1613 gtk_widget_show_all (uzbl
.gui
.main_window
);
1614 uzbl
.xwin
= GDK_WINDOW_XID (GTK_WIDGET (uzbl
.gui
.main_window
)->window
);
1615 printf("window_id %i\n",(int) uzbl
.xwin
);
1616 printf("pid %i\n", getpid ());
1617 printf("name: %s\n", uzbl
.state
.instance_name
);
1619 uzbl
.gui
.scbar_v
= (GtkScrollbar
*) gtk_vscrollbar_new (NULL
);
1620 uzbl
.gui
.bar_v
= gtk_range_get_adjustment((GtkRange
*) uzbl
.gui
.scbar_v
);
1621 uzbl
.gui
.scbar_h
= (GtkScrollbar
*) gtk_hscrollbar_new (NULL
);
1622 uzbl
.gui
.bar_h
= gtk_range_get_adjustment((GtkRange
*) uzbl
.gui
.scbar_h
);
1623 gtk_widget_set_scroll_adjustments ((GtkWidget
*) uzbl
.gui
.web_view
, uzbl
.gui
.bar_h
, uzbl
.gui
.bar_v
);
1627 if (!uzbl
.behave
.show_status
)
1628 gtk_widget_hide(uzbl
.gui
.mainbar
);
1637 return EXIT_SUCCESS
;
1640 /* vi: set et ts=4: */