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 { "verbose", 'v', 0, G_OPTION_ARG_NONE
, &uzbl
.state
.verbose
, "Whether to print all messages or just errors.", "VERBOSE" },
128 { "name", 'n', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.instance_name
, "Name of the current instance (defaults to Xorg window id)", "NAME" },
129 { "config", 'c', 0, G_OPTION_ARG_STRING
, &uzbl
.state
.config_file
, "Config file", "FILE" },
130 { NULL
, 0, 0, 0, NULL
, NULL
, NULL
}
133 typedef void (*Command
)(WebKitWebView
*, const char *);
136 static char *XDG_CONFIG_HOME_default
[256];
137 static char *XDG_CONFIG_DIRS_default
= "/etc/xdg";
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 //ADD HOVER URL TO WINDOW TITLE
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
},
412 { "exit", close_uzbl
},
413 { "search", search_text
},
414 { "insert_mode", set_insert_mode
},
422 uzbl
.behave
.commands
= g_hash_table_new(g_str_hash
, g_str_equal
);
424 for (i
= 0; i
< LENGTH(cmdlist
); i
++)
425 g_hash_table_insert(uzbl
.behave
.commands
, cmdlist
[i
].name
, cmdlist
[i
].command
);
428 /* -- CORE FUNCTIONS -- */
431 free_action(gpointer act
) {
432 Action
*action
= (Action
*)act
;
433 g_free(action
->name
);
435 g_free(action
->param
);
440 new_action(const gchar
*name
, const gchar
*param
) {
441 Action
*action
= g_new(Action
, 1);
443 action
->name
= g_strdup(name
);
445 action
->param
= g_strdup(param
);
447 action
->param
= NULL
;
453 file_exists (const char * filename
) {
454 return (access(filename
, F_OK
) == 0);
458 set_insert_mode(WebKitWebView
*page
, const gchar
*param
) {
462 uzbl
.behave
.insert_mode
= TRUE
;
467 load_uri (WebKitWebView
* web_view
, const gchar
*param
) {
469 GString
* newuri
= g_string_new (param
);
470 if (g_strrstr (param
, "://") == NULL
)
471 g_string_prepend (newuri
, "http://");
472 /* if we do handle cookies, ask our handler for them */
473 webkit_web_view_load_uri (web_view
, newuri
->str
);
474 g_string_free (newuri
, TRUE
);
479 run_js (WebKitWebView
* web_view
, const gchar
*param
) {
481 webkit_web_view_execute_script (web_view
, param
);
485 search_text (WebKitWebView
*page
, const char *param
) {
486 if ((param
) && (param
[0] != '\0')) {
487 strcpy(uzbl
.state
.searchtx
, param
);
489 if (uzbl
.state
.searchtx
[0] != '\0') {
490 if (uzbl
.state
.verbose
)
491 printf ("Searching: %s\n", uzbl
.state
.searchtx
);
492 webkit_web_view_unmark_text_matches (page
);
493 webkit_web_view_mark_text_matches (page
, uzbl
.state
.searchtx
, FALSE
, 0);
494 webkit_web_view_set_highlight_text_matches (page
, TRUE
);
495 webkit_web_view_search_text (page
, uzbl
.state
.searchtx
, FALSE
, TRUE
, TRUE
);
500 new_window_load_uri (const gchar
* uri
) {
501 GString
* to_execute
= g_string_new ("");
502 g_string_append_printf (to_execute
, "%s --uri '%s'", uzbl
.state
.executable_path
, uri
);
504 for (i
= 0; entries
[i
].long_name
!= NULL
; i
++) {
505 if ((entries
[i
].arg
== G_OPTION_ARG_STRING
) && (strcmp(entries
[i
].long_name
,"uri")!=0)) {
506 gchar
** str
= (gchar
**)entries
[i
].arg_data
;
508 g_string_append_printf (to_execute
, " --%s '%s'", entries
[i
].long_name
, *str
);
512 if (uzbl
.state
.verbose
)
513 printf("\n%s\n", to_execute
->str
);
514 g_spawn_command_line_async (to_execute
->str
, NULL
);
515 g_string_free (to_execute
, TRUE
);
519 close_uzbl (WebKitWebView
*page
, const char *param
) {
525 /* --Statusbar functions-- */
527 build_progressbar_ascii(int percent
) {
531 GString
*bar
= g_string_new("");
533 l
= (double)percent
*((double)width
/100.);
534 l
= (int)(l
+.5)>=(int)l
? l
+.5 : l
;
536 for(i
=0; i
<(int)l
; i
++)
537 g_string_append(bar
, "=");
540 g_string_append(bar
, "·");
542 return g_string_free(bar
, FALSE
);
547 const GScannerConfig scan_config
= {
550 ) /* cset_skip_characters */,
555 ) /* cset_identifier_first */,
562 ) /* cset_identifier_nth */,
563 ( "" ) /* cpair_comment_single */,
565 TRUE
/* case_sensitive */,
567 FALSE
/* skip_comment_multi */,
568 FALSE
/* skip_comment_single */,
569 FALSE
/* scan_comment_multi */,
570 TRUE
/* scan_identifier */,
571 TRUE
/* scan_identifier_1char */,
572 FALSE
/* scan_identifier_NULL */,
573 TRUE
/* scan_symbols */,
574 FALSE
/* scan_binary */,
575 FALSE
/* scan_octal */,
576 FALSE
/* scan_float */,
577 FALSE
/* scan_hex */,
578 FALSE
/* scan_hex_dollar */,
579 FALSE
/* scan_string_sq */,
580 FALSE
/* scan_string_dq */,
581 TRUE
/* numbers_2_int */,
582 FALSE
/* int_2_float */,
583 FALSE
/* identifier_2_string */,
584 FALSE
/* char_2_token */,
585 FALSE
/* symbol_2_token */,
586 TRUE
/* scope_0_fallback */,
591 uzbl
.scan
= g_scanner_new(&scan_config
);
592 while(symp
->symbol_name
) {
593 g_scanner_scope_add_symbol(uzbl
.scan
, 0,
595 GINT_TO_POINTER(symp
->symbol_token
));
601 expand_template(const char *template) {
602 if(!template) return NULL
;
604 GTokenType token
= G_TOKEN_NONE
;
605 GString
*ret
= g_string_new("");
609 g_scanner_input_text(uzbl
.scan
, template, strlen(template));
610 while(!g_scanner_eof(uzbl
.scan
) && token
!= G_TOKEN_LAST
) {
611 token
= g_scanner_get_next_token(uzbl
.scan
);
613 if(token
== G_TOKEN_SYMBOL
) {
614 sym
= (int)g_scanner_cur_value(uzbl
.scan
).v_symbol
;
619 g_markup_printf_escaped("%s", uzbl
.state
.uri
):"");
622 buf
= itos(uzbl
.gui
.sbar
.load_progress
);
623 g_string_append(ret
, buf
);
626 case SYM_LOADPRGSBAR
:
627 buf
= build_progressbar_ascii(uzbl
.gui
.sbar
.load_progress
);
628 g_string_append(ret
, buf
);
634 g_markup_printf_escaped("%s", uzbl
.gui
.main_title
):"");
637 buf
= itos(uzbl
.xwin
);
639 uzbl
.state
.instance_name
?uzbl
.state
.instance_name
:buf
);
644 uzbl
.state
.keycmd
->str
?
645 g_markup_printf_escaped("%s", uzbl
.state
.keycmd
->str
):"");
649 uzbl
.behave
.insert_mode
?"[I]":"[C]");
653 uzbl
.gui
.sbar
.msg
?uzbl
.gui
.sbar
.msg
:"");
657 buf
= itos(WEBKIT_MAJOR_VERSION
);
658 g_string_append(ret
, buf
);
662 buf
= itos(WEBKIT_MINOR_VERSION
);
663 g_string_append(ret
, buf
);
667 buf
= itos(WEBKIT_MICRO_VERSION
);
668 g_string_append(ret
, buf
);
672 g_string_append(ret
, uzbl
.state
.unameinfo
.sysname
);
675 g_string_append(ret
, uzbl
.state
.unameinfo
.nodename
);
678 g_string_append(ret
, uzbl
.state
.unameinfo
.release
);
681 g_string_append(ret
, uzbl
.state
.unameinfo
.version
);
684 g_string_append(ret
, uzbl
.state
.unameinfo
.machine
);
687 g_string_append(ret
, ARCH
);
691 g_string_append(ret
, uzbl
.state
.unameinfo
.domainname
);
695 g_string_append(ret
, COMMIT
);
701 else if(token
== G_TOKEN_INT
) {
702 buf
= itos(g_scanner_cur_value(uzbl
.scan
).v_int
);
703 g_string_append(ret
, buf
);
706 else if(token
== G_TOKEN_IDENTIFIER
) {
707 g_string_append(ret
, (gchar
*)g_scanner_cur_value(uzbl
.scan
).v_identifier
);
709 else if(token
== G_TOKEN_CHAR
) {
710 g_string_append_c(ret
, (gchar
)g_scanner_cur_value(uzbl
.scan
).v_char
);
714 return g_string_free(ret
, FALSE
);
716 /* --End Statusbar functions-- */
719 // make sure to put '' around args, so that if there is whitespace we can still keep arguments together.
721 run_command (const char *command
, const char *args
, const gboolean sync
, char **stdout
) {
722 //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
723 GString
* to_execute
= g_string_new ("");
725 g_string_printf (to_execute
, "%s '%s' '%i' '%i' '%s' '%s'",
726 command
, (uzbl
.state
.config_file
? uzbl
.state
.config_file
: "(null)"),
727 (int) getpid(), (int) uzbl
.xwin
, uzbl
.comm
.fifo_path
,
728 uzbl
.comm
.socket_path
);
729 g_string_append_printf (to_execute
, " '%s' '%s'",
730 uzbl
.state
.uri
, uzbl
.gui
.main_title
);
731 if(args
) g_string_append_printf (to_execute
, " %s", args
);
734 result
= g_spawn_command_line_sync (to_execute
->str
, stdout
, NULL
, NULL
, NULL
);
735 } else result
= g_spawn_command_line_async (to_execute
->str
, NULL
);
736 printf("Called %s. Result: %s\n", to_execute
->str
, (result
? "TRUE" : "FALSE" ));
737 g_string_free (to_execute
, TRUE
);
742 spawn(WebKitWebView
*web_view
, const char *param
) {
744 run_command(param
, NULL
, FALSE
, NULL
);
748 parse_command(const char *cmd
, const char *param
) {
751 if ((c
= g_hash_table_lookup(uzbl
.behave
.commands
, cmd
)))
752 c(uzbl
.gui
.web_view
, param
);
754 fprintf (stderr
, "command \"%s\" not understood. ignoring.\n", cmd
);
762 uzbl
.comm
.get_regex
= g_regex_new("^[Gg][a-zA-Z]*\\s+([^ \\n]+)$",
763 G_REGEX_OPTIMIZE
, 0, &err
);
764 uzbl
.comm
.set_regex
= g_regex_new("^[Ss][a-zA-Z]*\\s+([^ ]+)\\s*=\\s*([^\\n].*)$",
765 G_REGEX_OPTIMIZE
, 0, &err
);
766 uzbl
.comm
.bind_regex
= g_regex_new("^[Bb][a-zA-Z]*\\s+?(.*[^ ])\\s*?=\\s*([a-z][^\\n].+)$",
767 G_REGEX_UNGREEDY
|G_REGEX_OPTIMIZE
, 0, &err
);
768 uzbl
.comm
.act_regex
= g_regex_new("^[Aa][a-zA-Z]*\\s+([^ \\n]+)\\s*([^\\n]*)?$",
769 G_REGEX_OPTIMIZE
, 0, &err
);
770 uzbl
.comm
.keycmd_regex
= g_regex_new("^[Kk][a-zA-Z]*\\s+([^\\n]+)$",
771 G_REGEX_OPTIMIZE
, 0, &err
);
775 get_var_value(gchar
*name
) {
778 if( (p
= g_hash_table_lookup(uzbl
.comm
.proto_var
, name
)) ) {
779 if(var_is("status_format", name
)
780 || var_is("useragent", name
)) {
781 printf("VAR: %s VALUE: %s\n", name
, (char *)*p
);
782 } else printf("VAR: %s VALUE: %d\n", name
, (int)*p
);
791 if(*uzbl
.net
.proxy_url
== ' '
792 || uzbl
.net
.proxy_url
== NULL
) {
793 soup_session_remove_feature_by_type(uzbl
.net
.soup_session
,
794 (GType
) SOUP_SESSION_PROXY_URI
);
797 suri
= soup_uri_new(uzbl
.net
.proxy_url
);
798 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
799 SOUP_SESSION_PROXY_URI
,
809 gtk_widget_ref(uzbl
.gui
.scrolled_win
);
810 gtk_widget_ref(uzbl
.gui
.mainbar
);
811 gtk_container_remove(GTK_CONTAINER(uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
);
812 gtk_container_remove(GTK_CONTAINER(uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
);
814 if(uzbl
.behave
.status_top
) {
815 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
816 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
819 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
820 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
822 gtk_widget_unref(uzbl
.gui
.scrolled_win
);
823 gtk_widget_unref(uzbl
.gui
.mainbar
);
824 gtk_widget_grab_focus (GTK_WIDGET (uzbl
.gui
.web_view
));
828 var_is(const char *x
, const char *y
) {
829 return (strcmp(x
, y
) == 0 ? TRUE
: FALSE
);
833 set_var_value(gchar
*name
, gchar
*val
) {
837 if( (p
= g_hash_table_lookup(uzbl
.comm
.proto_var
, name
)) ) {
838 if(var_is("status_message", name
)
839 || var_is("status_background", name
)
840 || var_is("status_format", name
)
841 || var_is("load_finish_handler", name
)
842 || var_is("history_handler", name
)
843 || var_is("download_handler", name
)
844 || var_is("cookie_handler", name
)) {
850 else if(var_is("uri", name
)) {
853 load_uri(uzbl
.gui
.web_view
, (const gchar
*)*p
);
855 else if(var_is("proxy_url", name
)) {
860 else if(var_is("fifo_dir", name
)) {
862 *p
= init_fifo(g_strdup(val
));
864 else if(var_is("socket_dir", name
)) {
866 *p
= init_socket(g_strdup(val
));
868 else if(var_is("modkey", name
)) {
871 *p
= g_utf8_strup(val
, -1);
872 uzbl
.behave
.modmask
= 0;
873 for (i
= 0; modkeys
[i
].key
!= NULL
; i
++) {
874 if (g_strrstr(*p
, modkeys
[i
].key
))
875 uzbl
.behave
.modmask
|= modkeys
[i
].mask
;
878 else if(var_is("useragent", name
)) {
880 *p
= set_useragent(g_strdup(val
));
882 /* variables that take int values */
885 *ip
= (int)strtoul(val
, &endp
, 10);
887 if(var_is("show_status", name
)) {
890 else if(var_is("always_insert_mode", name
)) {
891 uzbl
.behave
.insert_mode
=
892 uzbl
.behave
.always_insert_mode
? TRUE
: FALSE
;
895 else if (var_is("max_conns", name
)) {
896 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
897 SOUP_SESSION_MAX_CONNS
, uzbl
.net
.max_conns
, NULL
);
899 else if (var_is("max_conns_host", name
)) {
900 g_object_set(G_OBJECT(uzbl
.net
.soup_session
),
901 SOUP_SESSION_MAX_CONNS_PER_HOST
, uzbl
.net
.max_conns_host
, NULL
);
903 else if (var_is("http_debug", name
)) {
904 //soup_session_remove_feature
905 // (uzbl.net.soup_session, uzbl.net.soup_logger);
906 soup_session_remove_feature
907 (uzbl
.net
.soup_session
, SOUP_SESSION_FEATURE(uzbl
.net
.soup_logger
));
908 /* do we leak if this doesn't get freed? why does it occasionally crash if freed? */
909 /*g_free(uzbl.net.soup_logger);*/
911 uzbl
.net
.soup_logger
= soup_logger_new(uzbl
.behave
.http_debug
, -1);
912 soup_session_add_feature(uzbl
.net
.soup_session
,
913 SOUP_SESSION_FEATURE(uzbl
.net
.soup_logger
));
915 else if (var_is("status_top", name
)) {
924 runcmd(WebKitWebView
* page
, const char *param
) {
926 parse_cmd_line(param
);
930 parse_cmd_line(const char *ctl_line
) {
934 if(ctl_line
[0] == 's' || ctl_line
[0] == 'S') {
935 tokens
= g_regex_split(uzbl
.comm
.set_regex
, ctl_line
, 0);
936 if(tokens
[0][0] == 0) {
937 set_var_value(tokens
[1], tokens
[2]);
941 printf("Error in command: %s\n", tokens
[0]);
944 else if(ctl_line
[0] == 'g' || ctl_line
[0] == 'G') {
945 tokens
= g_regex_split(uzbl
.comm
.get_regex
, ctl_line
, 0);
946 if(tokens
[0][0] == 0) {
947 get_var_value(tokens
[1]);
951 printf("Error in command: %s\n", tokens
[0]);
954 else if(ctl_line
[0] == 'b' || ctl_line
[0] == 'B') {
955 tokens
= g_regex_split(uzbl
.comm
.bind_regex
, ctl_line
, 0);
956 if(tokens
[0][0] == 0) {
957 add_binding(tokens
[1], tokens
[2]);
961 printf("Error in command: %s\n", tokens
[0]);
964 else if(ctl_line
[0] == 'A' || ctl_line
[0] == 'a') {
965 tokens
= g_regex_split(uzbl
.comm
.act_regex
, ctl_line
, 0);
966 if(tokens
[0][0] == 0) {
967 parse_command(tokens
[1], tokens
[2]);
971 printf("Error in command: %s\n", tokens
[0]);
974 else if(ctl_line
[0] == 'K' || ctl_line
[0] == 'k') {
975 tokens
= g_regex_split(uzbl
.comm
.keycmd_regex
, ctl_line
, 0);
976 if(tokens
[0][0] == 0) {
977 /* should incremental commands want each individual "keystroke"
978 sent in a loop or the whole string in one go like now? */
979 g_string_assign(uzbl
.state
.keycmd
, tokens
[1]);
986 else if( (ctl_line
[0] == '#')
987 || (ctl_line
[0] == ' ')
988 || (ctl_line
[0] == '\n'))
989 ; /* ignore these lines */
991 printf("Command not understood (%s)\n", ctl_line
);
997 build_stream_name(int type
, const gchar
* dir
) {
999 State
*s
= &uzbl
.state
;
1002 xwin_str
= itos((int)uzbl
.xwin
);
1004 str
= g_strdup_printf
1005 ("%s/uzbl_fifo_%s", dir
,
1006 s
->instance_name
? s
->instance_name
: xwin_str
);
1007 } else if (type
== SOCKET
) {
1008 str
= g_strdup_printf
1009 ("%s/uzbl_socket_%s", dir
,
1010 s
->instance_name
? s
->instance_name
: xwin_str
);
1017 control_fifo(GIOChannel
*gio
, GIOCondition condition
) {
1018 if (uzbl
.state
.verbose
)
1019 printf("triggered\n");
1024 if (condition
& G_IO_HUP
)
1025 g_error ("Fifo: Read end of pipe died!\n");
1028 g_error ("Fifo: GIOChannel broke\n");
1030 ret
= g_io_channel_read_line(gio
, &ctl_line
, NULL
, NULL
, &err
);
1031 if (ret
== G_IO_STATUS_ERROR
)
1032 g_error ("Fifo: Error reading: %s\n", err
->message
);
1034 parse_cmd_line(ctl_line
);
1041 init_fifo(gchar
*dir
) { /* return dir or, on error, free dir and return NULL */
1042 if (uzbl
.comm
.fifo_path
) { /* get rid of the old fifo if one exists */
1043 if (unlink(uzbl
.comm
.fifo_path
) == -1)
1044 g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl
.comm
.fifo_path
);
1045 g_free(uzbl
.comm
.fifo_path
);
1046 uzbl
.comm
.fifo_path
= NULL
;
1049 if (*dir
== ' ') { /* space unsets the variable */
1054 GIOChannel
*chan
= NULL
;
1055 GError
*error
= NULL
;
1056 gchar
*path
= build_stream_name(FIFO
, dir
);
1058 if (!file_exists(path
)) {
1059 if (mkfifo (path
, 0666) == 0) {
1060 // 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.
1061 chan
= g_io_channel_new_file(path
, "r+", &error
);
1063 if (g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_fifo
, NULL
)) {
1064 if (uzbl
.state
.verbose
)
1065 printf ("init_fifo: created successfully as %s\n", path
);
1066 uzbl
.comm
.fifo_path
= path
;
1068 } else g_warning ("init_fifo: could not add watch on %s\n", path
);
1069 } else g_warning ("init_fifo: can't open: %s\n", error
->message
);
1070 } else g_warning ("init_fifo: can't create %s: %s\n", path
, strerror(errno
));
1071 } else g_warning ("init_fifo: can't create %s: file exists\n", path
);
1073 /* if we got this far, there was an error; cleanup */
1080 control_stdin(GIOChannel
*gio
, GIOCondition condition
) {
1081 gchar
*ctl_line
= NULL
;
1082 gsize ctl_line_len
= 0;
1086 if (condition
& G_IO_HUP
) {
1087 ret
= g_io_channel_shutdown (gio
, FALSE
, &err
);
1091 ret
= g_io_channel_read_line(gio
, &ctl_line
, &ctl_line_len
, NULL
, &err
);
1092 if ( (ret
== G_IO_STATUS_ERROR
) || (ret
== G_IO_STATUS_EOF
) )
1095 parse_cmd_line(ctl_line
);
1103 GIOChannel
*chan
= NULL
;
1104 GError
*error
= NULL
;
1106 chan
= g_io_channel_unix_new(fileno(stdin
));
1108 if (!g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_stdin
, NULL
)) {
1109 g_error ("Stdin: could not add watch\n");
1111 printf ("Stdin: watch added successfully\n");
1114 g_error ("Stdin: Error while opening: %s\n", error
->message
);
1119 control_socket(GIOChannel
*chan
) {
1120 struct sockaddr_un remote
;
1121 char buffer
[512], *ctl_line
;
1123 int sock
, clientsock
, n
, done
;
1126 sock
= g_io_channel_unix_get_fd(chan
);
1128 memset (buffer
, 0, sizeof (buffer
));
1130 t
= sizeof (remote
);
1131 clientsock
= accept (sock
, (struct sockaddr
*) &remote
, &t
);
1135 memset (temp
, 0, sizeof (temp
));
1136 n
= recv (clientsock
, temp
, 128, 0);
1138 buffer
[strlen (buffer
)] = '\0';
1142 strcat (buffer
, temp
);
1145 if (strcmp (buffer
, "\n") < 0) {
1146 buffer
[strlen (buffer
) - 1] = '\0';
1148 buffer
[strlen (buffer
)] = '\0';
1151 ctl_line
= g_strdup(buffer
);
1152 parse_cmd_line (ctl_line
);
1155 TODO: we should be able to do it with this. but glib errors out with "Invalid argument"
1156 GError *error = NULL;
1159 ret = g_io_channel_read_line(chan, &ctl_line, &len, NULL, &error);
1160 if (ret == G_IO_STATUS_ERROR)
1161 g_error ("Error reading: %s\n", error->message);
1163 printf("Got line %s (%u bytes) \n",ctl_line, len);
1165 parse_line(ctl_line);
1173 init_socket(gchar
*dir
) { /* return dir or, on error, free dir and return NULL */
1174 if (uzbl
.comm
.socket_path
) { /* remove an existing socket should one exist */
1175 if (unlink(uzbl
.comm
.socket_path
) == -1)
1176 g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl
.comm
.socket_path
);
1177 g_free(uzbl
.comm
.socket_path
);
1178 uzbl
.comm
.socket_path
= NULL
;
1186 GIOChannel
*chan
= NULL
;
1188 struct sockaddr_un local
;
1189 gchar
*path
= build_stream_name(SOCKET
, dir
);
1191 sock
= socket (AF_UNIX
, SOCK_STREAM
, 0);
1193 local
.sun_family
= AF_UNIX
;
1194 strcpy (local
.sun_path
, path
);
1195 unlink (local
.sun_path
);
1197 len
= strlen (local
.sun_path
) + sizeof (local
.sun_family
);
1198 if (bind (sock
, (struct sockaddr
*) &local
, len
) != -1) {
1199 printf ("init_socket: opened in %s\n", path
);
1202 if( (chan
= g_io_channel_unix_new(sock
)) ) {
1203 g_io_add_watch(chan
, G_IO_IN
|G_IO_HUP
, (GIOFunc
) control_socket
, chan
);
1204 uzbl
.comm
.socket_path
= path
;
1207 } else g_warning ("init_socket: could not open in %s: %s\n", path
, strerror(errno
));
1209 /* if we got this far, there was an error; cleanup */
1216 update_title (void) {
1217 GString
* string_long
= g_string_new ("");
1218 GString
* string_short
= g_string_new ("");
1222 State
*s
= &uzbl
.state
;
1223 Behaviour
*b
= &uzbl
.behave
;
1225 if(s
->instance_name
) {
1226 iname_len
= strlen(s
->instance_name
)+4;
1227 iname
= malloc(iname_len
);
1228 snprintf(iname
, iname_len
, "<%s> ", s
->instance_name
);
1230 g_string_prepend(string_long
, iname
);
1231 g_string_prepend(string_short
, iname
);
1235 g_string_append_printf(string_long
, "%s ", s
->keycmd
->str
);
1236 if (!b
->always_insert_mode
)
1237 g_string_append (string_long
, (b
->insert_mode
? "[I] " : "[C] "));
1238 if (uzbl
.gui
.main_title
) {
1239 g_string_append (string_long
, uzbl
.gui
.main_title
);
1240 g_string_append (string_short
, uzbl
.gui
.main_title
);
1242 g_string_append (string_long
, " - Uzbl browser");
1243 g_string_append (string_short
, " - Uzbl browser");
1244 if (s
->selected_url
[0]!=0) {
1245 g_string_append_printf (string_long
, " -> (%s)", s
->selected_url
);
1248 gchar
* title_long
= g_string_free (string_long
, FALSE
);
1249 gchar
* title_short
= g_string_free (string_short
, FALSE
);
1251 if (b
->show_status
) {
1252 gtk_window_set_title (GTK_WINDOW(uzbl
.gui
.main_window
), title_short
);
1253 // TODO: we should probably not do this every time we want to update the title..?
1254 statln
= expand_template(uzbl
.behave
.status_format
);
1255 gtk_label_set_markup(GTK_LABEL(uzbl
.gui
.mainbar_label
), statln
);
1256 if (b
->status_background
) {
1258 gdk_color_parse (b
->status_background
, &color
);
1259 //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)
1260 gtk_widget_modify_bg (uzbl
.gui
.main_window
, GTK_STATE_NORMAL
, &color
);
1264 gtk_window_set_title (GTK_WINDOW(uzbl
.gui
.main_window
), title_long
);
1267 g_free (title_long
);
1268 g_free (title_short
);
1272 key_press_cb (WebKitWebView
* page
, GdkEventKey
* event
)
1274 //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
1278 if (event
->type
!= GDK_KEY_PRESS
|| event
->keyval
== GDK_Page_Up
|| event
->keyval
== GDK_Page_Down
1279 || 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
)
1282 /* turn off insert mode (if always_insert_mode is not used) */
1283 if (uzbl
.behave
.insert_mode
&& (event
->keyval
== GDK_Escape
)) {
1284 uzbl
.behave
.insert_mode
= uzbl
.behave
.always_insert_mode
;
1289 if (uzbl
.behave
.insert_mode
&& (((event
->state
& uzbl
.behave
.modmask
) != uzbl
.behave
.modmask
) || (!uzbl
.behave
.modmask
)))
1292 if (event
->keyval
== GDK_Escape
) {
1293 g_string_truncate(uzbl
.state
.keycmd
, 0);
1298 //Insert without shift - insert from clipboard; Insert with shift - insert from primary
1299 if (event
->keyval
== GDK_Insert
) {
1301 if ((event
->state
& GDK_SHIFT_MASK
) == GDK_SHIFT_MASK
) {
1302 str
= gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY
));
1304 str
= gtk_clipboard_wait_for_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
));
1307 g_string_append (uzbl
.state
.keycmd
, str
);
1314 if ((event
->keyval
== GDK_BackSpace
) && (uzbl
.state
.keycmd
->len
> 0)) {
1315 g_string_truncate(uzbl
.state
.keycmd
, uzbl
.state
.keycmd
->len
- 1);
1319 gboolean key_ret
= FALSE
;
1320 if ((event
->keyval
== GDK_Return
) || (event
->keyval
== GDK_KP_Enter
))
1322 if (!key_ret
) g_string_append(uzbl
.state
.keycmd
, event
->string
);
1324 run_keycmd(key_ret
);
1326 if (key_ret
) return (!uzbl
.behave
.insert_mode
);
1331 run_keycmd(const gboolean key_ret
) {
1332 /* run the keycmd immediately if it isn't incremental and doesn't take args */
1334 if ((action
= g_hash_table_lookup(uzbl
.bindings
, uzbl
.state
.keycmd
->str
))) {
1335 g_string_truncate(uzbl
.state
.keycmd
, 0);
1336 parse_command(action
->name
, action
->param
);
1340 /* try if it's an incremental keycmd or one that takes args, and run it */
1341 GString
* short_keys
= g_string_new ("");
1342 GString
* short_keys_inc
= g_string_new ("");
1344 for (i
=0; i
<(uzbl
.state
.keycmd
->len
); i
++) {
1345 g_string_append_c(short_keys
, uzbl
.state
.keycmd
->str
[i
]);
1346 g_string_assign(short_keys_inc
, short_keys
->str
);
1347 g_string_append_c(short_keys
, '_');
1348 g_string_append_c(short_keys_inc
, '*');
1350 gboolean exec_now
= FALSE
;
1351 if ((action
= g_hash_table_lookup(uzbl
.bindings
, short_keys
->str
))) {
1352 if (key_ret
) exec_now
= TRUE
; /* run normal cmds only if return was pressed */
1353 } else if ((action
= g_hash_table_lookup(uzbl
.bindings
, short_keys_inc
->str
))) {
1354 if (key_ret
) { /* just quit the incremental command on return */
1355 g_string_truncate(uzbl
.state
.keycmd
, 0);
1357 } else exec_now
= TRUE
; /* always exec incr. commands on keys other than return */
1361 GString
* parampart
= g_string_new (uzbl
.state
.keycmd
->str
);
1362 GString
* actionname
= g_string_new ("");
1363 GString
* actionparam
= g_string_new ("");
1364 g_string_erase (parampart
, 0, i
+1);
1366 g_string_printf (actionname
, action
->name
, parampart
->str
);
1368 g_string_printf (actionparam
, action
->param
, parampart
->str
);
1369 parse_command(actionname
->str
, actionparam
->str
);
1370 g_string_free (actionname
, TRUE
);
1371 g_string_free (actionparam
, TRUE
);
1372 g_string_free (parampart
, TRUE
);
1374 g_string_truncate(uzbl
.state
.keycmd
, 0);
1378 g_string_truncate(short_keys
, short_keys
->len
- 1);
1380 g_string_free (short_keys
, TRUE
);
1381 g_string_free (short_keys_inc
, TRUE
);
1388 GtkWidget
* scrolled_window
= gtk_scrolled_window_new (NULL
, NULL
);
1389 //main_window_ref = g_object_ref(scrolled_window);
1390 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
1392 g
->web_view
= WEBKIT_WEB_VIEW (webkit_web_view_new ());
1393 gtk_container_add (GTK_CONTAINER (scrolled_window
), GTK_WIDGET (g
->web_view
));
1395 g_signal_connect (G_OBJECT (g
->web_view
), "title-changed", G_CALLBACK (title_change_cb
), g
->web_view
);
1396 g_signal_connect (G_OBJECT (g
->web_view
), "load-progress-changed", G_CALLBACK (progress_change_cb
), g
->web_view
);
1397 g_signal_connect (G_OBJECT (g
->web_view
), "load-committed", G_CALLBACK (load_commit_cb
), g
->web_view
);
1398 g_signal_connect (G_OBJECT (g
->web_view
), "load-finished", G_CALLBACK (log_history_cb
), g
->web_view
);
1399 g_signal_connect (G_OBJECT (g
->web_view
), "load-finished", G_CALLBACK (load_finish_cb
), g
->web_view
);
1400 g_signal_connect (G_OBJECT (g
->web_view
), "hovering-over-link", G_CALLBACK (link_hover_cb
), g
->web_view
);
1401 g_signal_connect (G_OBJECT (g
->web_view
), "key-press-event", G_CALLBACK (key_press_cb
), g
->web_view
);
1402 g_signal_connect (G_OBJECT (g
->web_view
), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb
), g
->web_view
);
1403 g_signal_connect (G_OBJECT (g
->web_view
), "download-requested", G_CALLBACK (download_cb
), g
->web_view
);
1404 g_signal_connect (G_OBJECT (g
->web_view
), "create-web-view", G_CALLBACK (create_web_view_cb
), g
->web_view
);
1406 return scrolled_window
;
1413 g
->mainbar
= gtk_hbox_new (FALSE
, 0);
1415 /* keep a reference to the bar so we can re-pack it at runtime*/
1416 //sbar_ref = g_object_ref(g->mainbar);
1418 g
->mainbar_label
= gtk_label_new ("");
1419 gtk_label_set_selectable((GtkLabel
*)g
->mainbar_label
, TRUE
);
1420 gtk_label_set_ellipsize(GTK_LABEL(g
->mainbar_label
), PANGO_ELLIPSIZE_END
);
1421 gtk_misc_set_alignment (GTK_MISC(g
->mainbar_label
), 0, 0);
1422 gtk_misc_set_padding (GTK_MISC(g
->mainbar_label
), 2, 2);
1423 gtk_box_pack_start (GTK_BOX (g
->mainbar
), g
->mainbar_label
, TRUE
, TRUE
, 0);
1428 GtkWidget
* create_window () {
1429 GtkWidget
* window
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
1430 gtk_window_set_default_size (GTK_WINDOW (window
), 800, 600);
1431 gtk_widget_set_name (window
, "Uzbl browser");
1432 g_signal_connect (G_OBJECT (window
), "destroy", G_CALLBACK (destroy_cb
), NULL
);
1438 add_binding (const gchar
*key
, const gchar
*act
) {
1439 char **parts
= g_strsplit(act
, " ", 2);
1446 if (uzbl
.state
.verbose
)
1447 printf ("Binding %-10s : %s\n", key
, act
);
1448 action
= new_action(parts
[0], parts
[1]);
1450 if(g_hash_table_lookup(uzbl
.bindings
, key
))
1451 g_hash_table_remove(uzbl
.bindings
, key
);
1452 g_hash_table_insert(uzbl
.bindings
, g_strdup(key
), action
);
1458 get_xdg_var (XDG_Var xdg
) {
1459 const gchar
* actual_value
= getenv (xdg
.environmental
);
1460 const gchar
* home
= getenv ("HOME");
1462 gchar
* return_value
= str_replace ("~", home
, g_strdup (actual_value
));
1464 if (! actual_value
|| strcmp (actual_value
, "") == 0) {
1465 if (xdg
.default_value
) {
1466 return_value
= str_replace ("~", home
, g_strdup (xdg
.default_value
));
1468 return_value
= NULL
;
1472 return return_value
;
1476 find_xdg_file (int xdg_type
, char* filename
) {
1477 /* xdg_type = 0 => config
1478 xdg_type = 1 => data
1479 xdg_type = 2 => cache*/
1481 gchar
* xdg_config_home
= get_xdg_var (XDG
[0]);
1482 gchar
* xdg_data_home
= get_xdg_var (XDG
[1]);
1483 gchar
* xdg_cache_home
= get_xdg_var (XDG
[2]);
1484 gchar
* xdg_config_dirs
= get_xdg_var (XDG
[3]);
1485 gchar
* xdg_data_dirs
= get_xdg_var (XDG
[4]);
1486 gchar
* temporary_file
= (char *)malloc (1024);
1487 gchar
* temporary_string
= NULL
;
1491 strcpy (temporary_file
, xdg_config_home
);
1494 strcpy (temporary_file
, xdg_data_home
);
1497 strcpy (temporary_file
, xdg_cache_home
);
1499 strcat (temporary_file
, filename
);
1501 if (! file_exists (temporary_file
) && xdg_type
!= 2) {
1503 temporary_string
= (char *) strtok_r (xdg_config_dirs
, ":", &saveptr
);
1506 temporary_string
= (char *) strtok_r (xdg_data_dirs
, ":", &saveptr
);
1508 while (temporary_string
&& ! file_exists (temporary_file
)) {
1509 strcpy (temporary_file
, temporary_string
);
1510 strcat (temporary_file
, filename
);
1511 temporary_string
= (char * ) strtok_r (NULL
, ":", &saveptr
);
1515 if (file_exists (temporary_file
)) {
1516 return temporary_file
;
1525 State
*s
= &uzbl
.state
;
1526 Network
*n
= &uzbl
.net
;
1528 uzbl
.behave
.reset_command_mode
= 1;
1530 if (!s
->config_file
) {
1531 s
->config_file
= g_strdup (find_xdg_file (0, "/uzbl/config"));
1534 if (s
->config_file
) {
1535 GIOChannel
*chan
= NULL
;
1536 GError
*error
= NULL
;
1537 gchar
*readbuf
= NULL
;
1540 chan
= g_io_channel_new_file(s
->config_file
, "r", &error
);
1543 while (g_io_channel_read_line(chan
, &readbuf
, &len
, NULL
, NULL
)
1544 == G_IO_STATUS_NORMAL
) {
1545 parse_cmd_line(readbuf
);
1549 g_io_channel_unref (chan
);
1550 printf ("Config %s loaded\n", s
->config_file
);
1552 fprintf(stderr
, "uzbl: error loading file%s\n", s
->config_file
);
1555 printf ("No configuration file loaded.\n");
1557 if (!uzbl
.behave
.status_format
)
1558 uzbl
.behave
.status_format
= g_strdup(STATUS_DEFAULT
);
1560 g_signal_connect(n
->soup_session
, "request-queued", G_CALLBACK(handle_cookies
), NULL
);
1564 set_useragent(gchar
*val
) {
1569 gchar
*ua
= expand_template(val
);
1571 g_object_set(G_OBJECT(uzbl
.net
.soup_session
), SOUP_SESSION_USER_AGENT
, ua
, NULL
);
1575 static void handle_cookies (SoupSession
*session
, SoupMessage
*msg
, gpointer user_data
){
1578 if (!uzbl
.behave
.cookie_handler
) return;
1580 gchar
* stdout
= NULL
;
1581 soup_message_add_header_handler(msg
, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies
), NULL
);
1582 GString
* args
= g_string_new ("");
1583 SoupURI
* soup_uri
= soup_message_get_uri(msg
);
1584 g_string_printf (args
, "GET %s %s", soup_uri
->host
, soup_uri
->path
);
1585 run_command(uzbl
.behave
.cookie_handler
, args
->str
, TRUE
, &stdout
);
1587 soup_message_headers_replace (msg
->request_headers
, "Cookie", stdout
);
1589 g_string_free(args
, TRUE
);
1593 save_cookies (SoupMessage
*msg
, gpointer user_data
){
1597 for (ck
= soup_cookies_from_response(msg
); ck
; ck
= ck
->next
){
1598 cookie
= soup_cookie_to_set_cookie_header(ck
->data
);
1599 GString
* args
= g_string_new ("");
1600 SoupURI
* soup_uri
= soup_message_get_uri(msg
);
1601 g_string_printf (args
, "PUT %s %s \"%s\"", soup_uri
->host
, soup_uri
->path
, cookie
);
1602 run_command(uzbl
.behave
.cookie_handler
, args
->str
, FALSE
, NULL
);
1603 g_string_free(args
, TRUE
);
1611 main (int argc
, char* argv
[]) {
1612 gtk_init (&argc
, &argv
);
1613 if (!g_thread_supported ())
1614 g_thread_init (NULL
);
1616 strcpy(uzbl
.state
.executable_path
,argv
[0]);
1618 GError
*error
= NULL
;
1619 GOptionContext
* context
= g_option_context_new ("- some stuff here maybe someday");
1620 g_option_context_add_main_entries (context
, entries
, NULL
);
1621 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
1622 g_option_context_parse (context
, &argc
, &argv
, &error
);
1623 /* initialize hash table */
1624 uzbl
.bindings
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, free_action
);
1626 uzbl
.net
.soup_session
= webkit_get_default_session();
1627 uzbl
.state
.keycmd
= g_string_new("");
1629 if(setup_signal(SIGTERM
, catch_sigterm
) == SIG_ERR
)
1630 fprintf(stderr
, "uzbl: error hooking SIGTERM\n");
1631 if(setup_signal(SIGINT
, catch_sigint
) == SIG_ERR
)
1632 fprintf(stderr
, "uzbl: error hooking SIGINT\n");
1634 if(uname(&uzbl
.state
.unameinfo
) == -1)
1635 g_printerr("Can't retrieve unameinfo. Your useragent might appear wrong.\n");
1640 make_var_to_name_hash();
1643 uzbl
.gui
.vbox
= gtk_vbox_new (FALSE
, 0);
1645 uzbl
.gui
.scrolled_win
= create_browser();
1648 /* initial packing */
1649 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.scrolled_win
, TRUE
, TRUE
, 0);
1650 gtk_box_pack_start (GTK_BOX (uzbl
.gui
.vbox
), uzbl
.gui
.mainbar
, FALSE
, TRUE
, 0);
1652 uzbl
.gui
.main_window
= create_window ();
1653 gtk_container_add (GTK_CONTAINER (uzbl
.gui
.main_window
), uzbl
.gui
.vbox
);
1655 load_uri (uzbl
.gui
.web_view
, uzbl
.state
.uri
); //TODO: is this needed?
1657 gtk_widget_grab_focus (GTK_WIDGET (uzbl
.gui
.web_view
));
1658 gtk_widget_show_all (uzbl
.gui
.main_window
);
1659 uzbl
.xwin
= GDK_WINDOW_XID (GTK_WIDGET (uzbl
.gui
.main_window
)->window
);
1661 if (uzbl
.state
.verbose
) {
1662 printf("Uzbl start location: %s\n", argv
[0]);
1663 printf("window_id %i\n",(int) uzbl
.xwin
);
1664 printf("pid %i\n", getpid ());
1665 printf("name: %s\n", uzbl
.state
.instance_name
);
1668 uzbl
.gui
.scbar_v
= (GtkScrollbar
*) gtk_vscrollbar_new (NULL
);
1669 uzbl
.gui
.bar_v
= gtk_range_get_adjustment((GtkRange
*) uzbl
.gui
.scbar_v
);
1670 uzbl
.gui
.scbar_h
= (GtkScrollbar
*) gtk_hscrollbar_new (NULL
);
1671 uzbl
.gui
.bar_h
= gtk_range_get_adjustment((GtkRange
*) uzbl
.gui
.scbar_h
);
1672 gtk_widget_set_scroll_adjustments ((GtkWidget
*) uzbl
.gui
.web_view
, uzbl
.gui
.bar_h
, uzbl
.gui
.bar_v
);
1676 if (!uzbl
.behave
.show_status
)
1677 gtk_widget_hide(uzbl
.gui
.mainbar
);
1686 return EXIT_SUCCESS
;
1689 /* vi: set et ts=4: */